缓存的用户类vs单独的类,包含必要的缓存字段

时间:2014-02-05 07:10:33

标签: php oop caching

我需要缓存一些有关登录用户的信息(例如安全组,名称,用户名等)

目前我有一个单独的类来实现这一点,我们称之为CurrentUserHelper。给定一个用户对象,它将缓存适当的数据并将商店信息保存在$ _SESSION变量中。

问题是我发现一大堆只依赖于CurrentUserHelper的类,因为它们只需要几个公共字段。实际上,大多数函数与我的User类具有相同的名称。 CurrentUserHelper中有几个函数,例如getSecurityGroupsNames(),它包含所有安全组名称的缓存,但是这个函数名也没有理由不在用户类中。

相反,我应该创建一个CachedUser类并传递它吗?这个类可以扩展User,但是我可以覆盖getName(),getSecurityGroups()等,并返回缓存的数据,而不是预先形成db请求来获取数据。

传递CachedUser对象的缺点是,如果构造函数/函数接受类型User,这种情况会隐藏数据并不是最新的。我还需要找到处理将实体与Doctrine 2合并的方法,并确保将自己与CachedUser相关联的实体不会中断。如果我决定缓存一些临时数据(例如登录后的页面视图数),则此属性不应该是User类的一部分,而是更多关于当前用户的会话。

如果我继续使用CurrentUserHelper类,也许我应该创建一个接口并同时拥有CurrentUserHelperUser两个类共享的常用功能?

1 个答案:

答案 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;
    }
}

非常简单,我们使用其他缓存功能包装原始对象和方法调用。关于这一点很酷的是,你不仅可以随时为非缓存版本切换这个缓存版本(因为它们都依赖于相同的界面),但是你可以完全删除缓存,仅仅是更改实例化此对象的方式(您可以查看工厂模式,甚至根据配置变量决定 工厂(抽象工厂?))。