Какой шаблон я должен использовать для своего уникального экземпляра класса User? - PullRequest
3 голосов
/ 21 мая 2011

У меня есть этот класс пользователя

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    
}

Ну, теперь, учитывая, что я не могу зарегистрировать более одного пользователя (конечно, потому что мы говорим об одном запросе http) в Где я должен сохранить ссылкумоей истории?

Это тот случай, когда синглтон был бы полезен, но в наши дни все говорят, что синглтон - это зло (как статические методы ).

Я мог бы сделать $GLOBALS['currentUser'] = new User(); и сделать его доступным везде, но я думаю, что это хуже, чем синглтон.

Так что я могу сделать?
Обратите внимание, мне не нужно сохранять этот экземпляр между запросами. Мне просто нужен способ доступа к этому экземпляру в моей структуре в рамках того же запроса .

Если вы хотите знать, что я делаю сейчас для всех моих вспомогательных объектов, это служебный контейнер (этосчитается также плохим):

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

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

    return $refs[$class];
}

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

(то есть, что symfony делает )

Ответы [ 6 ]

6 голосов
/ 25 мая 2011

Шаблоны должны быть полезным руководством, подобно библиотеке ранее успешных программных абстракций. В наши дни слишком часто люди рассматривают паттерны как некую религию, в которой все «правильно» или «неправильно» независимо от контекста программы.

Подумайте, чего вы хотите достичь, и составьте карту так, чтобы это имело для вас смысл. Трахаться с мелкими различиями между этим шаблоном и этим шаблоном упускает смысл, и ваша программа не будет написана. Учитесь , делая!

НТН.

3 голосов
/ 23 мая 2011

Одиночки не являются злом.Плохое использование синглетонов - зло.Причина, по которой людям так сильно не нравится этот шаблон (даже если его можно назвать антипаттерном, каким бы он ни был), заключается в неправильном использовании:

Слишком много неопытных людей делают классСинглтон, когда они находят, им не нужно больше одного экземпляра класса.Но вопрос не в том, нужен ли вам только один экземпляр класса, а в том, не нарушит ли ваш код более одного экземпляра.Поэтому задайте себе этот вопрос: сломался бы ваш код, если бы было больше пользовательских экземпляров?Если нет, то, возможно, вам не стоит беспокоиться.:)

Есть законное использование синглетонов.Есть люди, которые боятся этого паттерна, как чума, и считают его всегда плохим, даже не осознавая, что иногда он может быть очень полезным.По словам гораздо более опытного программиста, чем я, «синглтоны похожи на морфин: они могут дать вам реальный импульс, но использовать их неправильно, и они сами станут проблемой».Если вы хотите, чтобы я подробно рассказал, когда синглтоны могут быть хорошим выбором, оставьте комментарий к этому ответу.:)

2 голосов
/ 25 мая 2011

Всегда сложно ответить на архитектурные вопросы без контекста.В этом случае очень важно, как объекты 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 обеспечивает создание экземпляра вашего сервиса только один раз.

Статья " Дизайн приложения, управляемого контейнером, Prelude: Куда относится контейнер? " может прояснить некоторые концепции DI.

1 голос
/ 21 мая 2011

Влияние Миско Хевери очень сильно на меня. Так же как и его новое - инъекционное различие. Пользователь не инъекционный, а новый. Каковы обязанности пользователя: сможет ли он сказать о себе, вошел он в систему или нет? Есть его пост, где он рассказывает о схожей проблеме: кредитной карте и ее списании (само?). Это пост о синглетах, что бы вы хотели сделать:

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

Это позволит службе проверить, вошел ли пользователь в систему или нет, какие права он имеет на сайте.

Это также означает, что ваша архитектура изменится, ваша проблема станет другой (передача пользователя? Где он нужен ?, как у вас будет доступ к услуге «проверка пользователя вошел в систему», ...).

1 голос
/ 21 мая 2011

Не уверен, почему все спорят наверх.Мне кажется, это вполне разумный вопрос.

Ключом здесь является использование статических членов класса 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();
0 голосов
/ 26 мая 2011
  1. , так как все остальные оценивают это, одиночки не являются злом.Я даже прочитал статью «Вруны», и он использует надуманный пример немодульного дизайна и плохого наследования зависимостей.

Я думаю, вы должны рассмотреть шаблон синглтон-фабрики, где синглтон-фабрика (Auth) предоставляет метод 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();
}
...

вам просто нужно убедиться, что вы запускаете функции сохранения в конце выполнения ... возможно, в деструкторе ()

simple

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...