ООП Видимость, в целом и конкретно в PHP - PullRequest
0 голосов
/ 30 января 2012

Это, вероятно, общий вопрос ООП, но я использую PHP, поэтому, если он вообще существует, я хотел бы знать, но предупреждаю, если вы знаете, что он отсутствует или искажен в PHP.

Возможно ли для класса, который создает экземпляр объекта другого класса (который он не расширяет), установить видимость свойств экземпляра объекта?

Пример:

Class Widget {

    public $property1;
    public $property2;
    public $property3;
  }

Class ClockMaker {

  public function getWidget() {

      $this -> widget = new Widget();
      $this -> widget -> property1 = "special stuff";
   }
}

$my_clock = new ClockMaker();
$my_clock -> getWidget();
$my_clock -> widget -> property2 = "my tweak";  // Totally fine and expected...
$my_clock -> widget -> property1 = "my stuff";  // Should throw an error...

Насколько я понимаю, если установить свойство1 в защищенное, ClockMaker не сможет установить значение.Но если в нынешнем виде $ my_clock может справиться с объектом виджета.

В любом случае, чтобы предотвратить установку свойства после его установки?

Ответы [ 3 ]

1 голос
/ 30 января 2012

Ваш код не меняет никаких свойств, и getWidget никогда не вызывается.Он создаст свойство widget в вашем $my_clock объекте, а затем создаст другой объект из пустого значения (если вы включите строгие ошибки, вы увидите строгое уведомление).Объявите widget в своем классе:

Class ClockMaker {
  private $widget;

  public function getWidget() {

      $this -> widget = new Widget();
      $this -> widget -> property1 = "special stuff";
   }
}
0 голосов
/ 30 января 2012

Если класс объявляет что-то публичное, то оно публичное. Если вы не собираетесь, чтобы член класса был общедоступным, он должен быть объявлен защищенным или закрытым. Если вы не прибегаете к самоанализу и рефлексии (что настоятельно не рекомендуется, потому что это обычно приводит к спагетти-коду и приводит к гибели производительности), то единственный способ изменить видимость члена - это наследовать класс в подклассе и переопределить член с помощью новая видимость. Когда вы делаете это, вы только можете сделать члена более видимым, чем это было раньше (закрытый участник может стать общедоступным, но не наоборот).

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

Однако, если внешним агентам по-прежнему требуется доступ к нему, вам следует предоставить метод получения и установки для этой работы. Получатель возвращает значение свойства, а установщик позволяет изменять его контролируемым образом. Методы получения и установки позволяют вам делать такие вещи, как сделать свойство доступным только для чтения, или реализовать ленивую инициализацию (это означает, что если классу нужно выполнить какую-то дорогую операцию инициализировать свойство, такое как поиск в БД, затем его можно отложить до фактического значения)

class Widget ()
{
    protected $property2;

    public function getProperty2 ()
    {
        // This is an example of lazy initialization in a getter.
        if ($this -> property2 === NULL)
        {
            $this -> property2 = 'Value has been initialized'
        }
        return ($this -> property2);
    }

    public function setProperty2 ($newProp)
    {
        // Example of input validation
        if {($newProp !== NULL) && ($newProp !== 'This is an illegal value!')}
        {
            $this -> property2 = $newProp;
        }
        else
        {
            throw new InvalidArgumentException;
        }
        return ($this);
    }
}

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

Между прочим, в большинстве случаев для класса инициализировать классы, от которых он зависит, считается плохой практикой. Это делает модульное тестирование более сложным (поскольку вы не можете протестировать класс, который инициализирует свои собственные зависимости изолированно) и может сделать ваш код менее понятным (поскольку зависимость, скрытая внутри класса, не сразу очевидна для тех, кто использует ваш класс с только публичный API в качестве руководства). Это также отнимает у вас некоторую гибкость, потому что если класс инициализирует свои собственные зависимые объекты, то вы не можете заменить их другими объектами, которые предоставляют тот же интерфейс. Есть исключения из правила (например, генерация исключений при возникновении ошибок), но, как правило, класс не должен содержать внутри себя оператор new и не должен извлекать объекты с помощью классов Registry или Singleton. Им также следует избегать статических вызовов в другие классы.

Лучше использовать взамен Dependency Injection .

Class Clockmaker 
{
    protected $widget;

    public function __construct (Widget $newWidget)
    {
        $this -> widget = $newWidget;
    }
}

Почему это лучше? Теперь ответ очевиден: класс ClockMaker зависит от класса Widget, поэтому он улучшает читаемость. Это также означает, что вы можете без проблем использовать любой объект, который является подклассом Widget, вместо самого Widget. Если вы выполняете модульное тестирование, вы можете заменить Widget на подкласс, где все методы все время возвращают одно и то же значение, так что теперь вы можете тестировать свой класс ClockMaker изолированно (если во время теста происходит ошибка, вы можете быть уверены, что ошибка в ClockMaker. Поскольку ClockMaker инициализирует сам Widget, вы не знаете, является ли неудачный тест следствием ошибки в Clockmaker или ошибки в Widget).

0 голосов
/ 30 января 2012

Почему бы не использовать методы get_ и set_?Похоже, это должно решить ваши проблемы.Это также лучшие практики в ООП для воспроизведения свойств объекта вне самого объекта.

class Widget {
  private $foo;

  public function setFoo($val) { $this->foo = $val; )
  public function getFoo() { return $this->foo; )
}

Вы также спросили: «В любом случае, чтобы предотвратить установку свойства после его установки?»

class Widget {
  private $foo;

  public function setFoo($val) {
    if (!isset($this->foo)) {
      $this->foo = $val;
    }
    //If you want to throw an error or something just use an else clause here.
  }
  public function getFoo() { return $this->foo; )
}

$w = new Widget();
$w->setFoo('Foooooo');
echo $w->getFoo(); //Foooooo
$w->setFoo('Magic.');
echo $w->getFoo(); //Foooooo

Если вы ищете что-то еще, не могли бы вы сделать вопрос более конкретным?:)

...