我应该将哪种模式用于我的User类的唯一实例?

时间:2011-05-20 23:33:50

标签: php oop design-patterns

我有这个用户类

class User{
  private    $logged = false;
  private    $id;

  public function User() {
      //> Check if the user is logged in with a cookie-database and set $logged=true;
  }  

  public function isLogged() {}
  public function editPerms() {}

  //> other methods    
}

现在考虑我记录的用户不能超过1个(当然因为我们正在讨论单个http请求)我应该在哪里存储我的费用?

这是单身有用的情况,但现在每个人都说单身是邪恶的(比如静态方法)。

我可以做$GLOBALS['currentUser'] = new User();并且可以随处访问,但我认为这比单身人士更糟糕。

那我该怎么办? 请注意,我不需要在请求之间保存此实例。 我只需要在同一个请求中在我的框架中访问此实例

如果你想知道我现在所做的所有Helper对象是一个服务容器(这被认为是坏的):

function app($class) {      //> Sample
    static $refs = array();

    if (!isset($refs[$class]))
        $refs[$class] = new $class();

    return $refs[$class];
}

//> usage app('User')->methods();

(IE是什么symfony does

6 个答案:

答案 0 :(得分:6)

模式应该是一个有用的指南,就像以前成功的软件抽象库一样。如今,人们常常将模式视为某种宗教,无论程序的背景如何,事物都是“正确”或“错误”。

想想你想要实现的目标,并以对你有意义的方式进行映射。在这种模式与该模式之间存在细微差别,并且忽略了这一点,并且它不会让您的程序得以编写。通过做来学习

HTH。

答案 1 :(得分:3)

单身人士不是邪恶的。单身人士的坏习惯是邪恶的。人们不喜欢这种模式的原因(甚至将其称为反模式,无论是什么),都是由于使用不当造成的:

太多没有经验的人在发现他们不需要一个班级的实例时,他们会使一个班级成为一个单身人士。但问题不在于你是否只需要一个类的实例,而是一个实例是否会破坏你的代码。所以问自己这个问题:如果有更多用户实例,你的代码会中断吗?如果没有,那么也许你不应该打扰。 :)

单身人士有合法用途。有些人像瘟疫一样害怕这种模式,并认为它总是很糟糕,没有意识到有时它会非常有用。用一个比我更有经验的程序员的话来说,“单身人士就像吗啡:他们可以给你一个真正的提升,但是以错误的方式使用它们而且它们本身就成了一个问题”。如果您希望我详细了解单身人士何时成为一个不错的选择,请对此答案发表评论。 :)

答案 2 :(得分:2)

如果没有上下文,很难回答架构问题。在这种情况下,如何持久化User对象(它们来自哪里?)以及如何组织客户端代码非常重要。我将假设一个MVC架构,因为它现在很流行。此外,我认为您的用户对象将承担更多的责任仅作为身份验证(您在此处提到了一些权限控制,但它仍然不够清晰)。

我会将身份验证责任推送到服务,并根据需要传递它。这是一些示例代码。

class AuthenticationService {
    /**
     * @var User
     */
    private $currentUser;

    public function __construct(Request $request) {
        // check if the request has an user identity
        // create a user object or do nothing otherwise
    }

    public function getCurrentUser() {
        return $this->currentUser;
    }
}

class User {
    public function editPerms(){}
}

// the client code
class Controller {
    private $auth;

    public function __construct(AuthenticationService $auth) {
        $this->auth = $auth;
    }

    public function handleRequest() {
        $currentUser = $this->auth->getCurrentUser();

        if ($currentUser === null) { // of course you could use Null Object Pattern
            // no user is logged in
        }

        // do something with the user object
    }
}

因此,您的问题的答案是:您需要在整个应用程序中进行适当的依赖注入。您从服务器获得的唯一对象是请求。依赖注入容器将其注入AuthenticationService,后者将注入到您的控制器中。没有单身,没有静态方法,没有全局变量。在DI容器中跟踪依赖关系并根据需要注入。 DI容器也确保您的服务仅实例化一次。

文章“Container-Managed Application Design, Prelude: Where does the Container Belong?”可能会澄清一些DI概念。

答案 3 :(得分:1)

不确定为什么所有人都争论不休。对我来说似乎是一个非常合理的问题。

这里的关键是使用User类的静态成员。无论有人说什么,静态方法都是你的朋友:

class User
{
   private    $logged = false;
   private    $id;

   private static $_currentUser;
   public static function currentUser()
   {
     if (empty(self::$_currentUser))
     {
         @session_start();
         if (array_key_exists('current_user', $_SESSION))
         {
             self::$_currentUser = $_SESSION['current_user'];
         }
         else
         {
           // force login in or whatever else.
           // if you log in, make sure to call User::_setCurrentUser(); 
             return null; //or some special 'empty' user.
         }
     }
     return self::$_currentUser;
  }
  // you may consider making this public, but it is private because it is a bit
  // more secure that way.  
  private static function _setCurrentUser(User $user)
  {
     self::$_currentUser = $user;
     $_SESSION['current_user'] = $user;
  }

  public function User() {
   //> Check if the user is logged in with a cookie-database and set $logged=true;
  }  

  public function isLogged() {}
  public function editPerms() {}

  //> other methods    
}

// Usage
$pUser = User::currentUser();

答案 4 :(得分:1)

Misko Hevery对我的影响非常强烈。他新的 - 可注射的区别也是如此。用户不是可注射的,而是新的。用户的责任是什么:他是否能够告诉自己他是否已登录?他有一个帖子,他谈到类似的问题:信用卡和收费(自我?)。它恰好是关于单身人士的帖子,你想做什么:

http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/

这会留给服务来检查用户是否已登录,以及他在网站上拥有的权利。

这也意味着您的架构会发生变化,您的问题会变得不同(绕过用户?,需要哪里?),您将如何访问'检查用户登录'服务,......)。

答案 5 :(得分:0)

  1. 因为其他人都在为此而努力,单身人士并不邪恶。 我甚至阅读了“骗子”文章,他正在使用一个非模块化设计和差的依赖继承的人为例子。
  2. 我认为您应该考虑单件工厂模式,其中单件工厂(Auth)提供返回User类的login()方法,以及在该User上的HTTP请求之间保存状态的方法。

    这将具有将安全性和会话功能与用户功能分开的好处。另外,使用工厂,您可以拥有多种类型的用户,而系统的其余部分无需在检查数据库之前了解要请求的对象

    class auth {
        private static $auth = null;
        private $user = null;
    
        // must use getAuth();
        private __construct(){};
    
        public getAuth() {
            if (is_null($this->auth) {
                 $this->auth = new auth();
            }
            return $this->auth;       
        }
    
        public function login($user,$pass) {
            ... // check db for user,
    
            if ($dbrow->user_type == 'admin') {
                $this->user = new admin_user($dbrow);
            } else {
                $this->user = new normal_user($dbrow);
            }
    
            $this->user->setSession($db->getsession());
        }
    
        public function getUser() {
            return $this->user;
        }
    
        public function saveSession() {
            // store $this->user session in db
        }
    
        public function saveUser() {
            // store $this->user changes in db
        }
        ...
    }
    

    用户类本身就变成了一个数据结构,只是强制执行安全和业务规则,并且可能为了输出目的而格式化一些数据。

    class normal_user extends user {
        ... getters and setters
        public function getName() {}
        public function setEmail() {}
        public function setprofile() {}
    }
    

    所有数据库,状态和安全问题都集中在身份验证中。 创建用户对象(合法)的唯一方法是运行auth-> login()。

    你仍被允许做

    $me = new normal_user();
    $me->setName();
    echo $me->getName();
    

    但新编码器无法将其保存在数据库中,因为它未在$ auth-> user中引用;

    然后,您可以在auth中创建一个函数来使用用户对象来创建新用户(在注册时)

    ...
    public function create(user $user) {
        // validate $user
        $this->user = $user;
        $this->saveUser();
    }
    ...
    

    你只需要确保在执行结束时运行保存功能...... 可能在析构函数()

    简单

相关问题