我需要缓存一些有关登录用户的信息(例如安全组,名称,用户名等)
目前我有一个单独的类来实现这一点,我们称之为CurrentUserHelper
。给定一个用户对象,它将缓存适当的数据并将商店信息保存在$ _SESSION变量中。
问题是我发现一大堆只依赖于CurrentUserHelper
的类,因为它们只需要几个公共字段。实际上,大多数函数与我的User
类具有相同的名称。 CurrentUserHelper
中有几个函数,例如getSecurityGroupsNames(),它包含所有安全组名称的缓存,但是这个函数名也没有理由不在用户类中。
相反,我应该创建一个CachedUser
类并传递它吗?这个类可以扩展User
,但是我可以覆盖getName(),getSecurityGroups()等,并返回缓存的数据,而不是预先形成db请求来获取数据。
传递CachedUser
对象的缺点是,如果构造函数/函数接受类型User
,这种情况会隐藏数据并不是最新的。我还需要找到处理将实体与Doctrine 2合并的方法,并确保将自己与CachedUser相关联的实体不会中断。如果我决定缓存一些临时数据(例如登录后的页面视图数),则此属性不应该是User
类的一部分,而是更多关于当前用户的会话。
如果我继续使用CurrentUserHelper
类,也许我应该创建一个接口并同时拥有CurrentUserHelper
和User
两个类共享的常用功能?
答案 0 :(得分:0)
前言:扩展不是这类事情的最佳方式..我相信你已经听到composition over inheritance一遍又一遍地对你大声喊叫。事实上,你甚至可以使用
extends
来获得的继承!
这听起来像是decorator pattern的一个很好的用例。基本上,您使用另一个实现相同接口的对象来包装现有对象,因此它具有与内部对象相同的方法,但是此对象的方法将方法调用周围的额外内容添加到内部对象,例如缓存。
想象一下,你有UserRepository
:
/**
* Represents an object capable of providing a user from persistence
*/
interface UserProvider
{
/**
* @param int $id
*
* @return User
*/
public function findUser($id);
}
/**
* Our object that already does the stuff we want to do, without caching
*/
class UserRepository implements UserProvider
{
/**
* @var DbAbstraction
*/
protected $db;
/**
* @var UserMapper
*/
protected $mapper;
/**
* @param DbAbstraction $db
* @param UserMapper $mapper
*/
public function __construct(DbAbstraction $db, UserMapper $mapper)
{
$this->db = $db;
$this->mapper = $mapper;
}
/**
* {@inheritDoc}
*/
public function findUser($id)
{
$data = $this->db->find(['id' => $id]);
/** Data mapper pattern - map data to the User object **/
$user = $this->mapper->map($data);
return $user;
}
}
以上是一个非常简单的例子。它将从其持久性(数据库,文件系统等)中检索用户数据,将该数据映射到实际的User
对象,然后将其返回。
很好,所以现在我们要添加缓存。我们应该在UserRepository
范围内执行此操作? 否,因为它不存储库执行缓存的责任,即使您依赖注入缓存对象(甚至是记录器!)..这是装饰器的地方模式会有用。
/**
* Decorates the UserRepository to provide caching
*/
class CachedUserRepository implements UserProvider
{
/**
* @var UserRepository
*/
protected $repo;
/**
* @var CachingImpl
*/
protected $cache;
/**
* @param UserRepository $repo
*/
public function __construct(UserRepository $repo, CachingImpl $cache)
{
$this->repo = $repo;
$this->cache = $cache;
}
/**
* {@inheritDoc}
*
* So, because this class also implements UserProvider, it has to
* have the same method in the interface. We FORWARD the call to
* the ACTUAL user provider, but put caching AROUND it...
*/
public function findUser($id)
{
/** Has this been cached? **/
if ($this->cache->hasKey($id))
{
/**
* Returns your user object, or maps data or whatever
*/
return $this->cache->get($id);
}
/** Hasn't been cached, forward the call to our user repository **/
$user = $this->repo->findUser($id);
/** Store the user in the cache for next time **/
$this->cache->add($id, $user);
return $user;
}
}
非常简单,我们使用其他缓存功能包装原始对象和方法调用。关于这一点很酷的是,你不仅可以随时为非缓存版本切换这个缓存版本(因为它们都依赖于相同的界面),但是你可以完全删除缓存,仅仅是更改实例化此对象的方式(您可以查看工厂模式,甚至根据配置变量决定 工厂(抽象工厂?))。