Ленивая загрузка классов методов в PHP - PullRequest
10 голосов
/ 22 июня 2011

У меня есть класс с несколькими довольно большими методами. В базовом и наиболее распространенном состоянии большая часть функциональности не требуется, поэтому мне было интересно, есть ли способ ленивой загрузки только частей класса. Методы должны иметь возможность доступа к закрытым / защищенным членам, поэтому было бы идеально, если бы методы были родными для класса, однако при поиске других решений я наткнулся на this , в котором обсуждается использование закрытых членов в обратных вызовах, которые было бы работоспособным решением (я бы использовал отдельные классы, которые содержат функцию, которая вызывает обратный вызов и ленивую загрузку этого класса). Это был 2009 год, и была ли удалена эта функциональность в более поздних версиях PHP, я не знаю, но, похоже, он не работает здесь с 5.3.5

Есть ли способ сделать это, или у вас есть какие-либо предложения для других шаблонов, на которые я должен обратить внимание?

Спасибо.


Вау! Спасибо за ответы на все вопросы. Я думаю, что некоторые из вас высказывают мнение, что это скорее преждевременная оптимизация, или, что еще хуже, совсем не оптимизация, и я буду заниматься профилированием, чтобы проверить, что любое решение, на котором я остановлюсь, действительно помогает не навредить. ... Теперь, чтобы прочитать и переварить все ваши мысли правильно. Еще раз спасибо.

Ответы [ 8 ]

6 голосов
/ 22 июня 2011

Начиная с PHP 5.4, вы можете (пере) связывать анонимные функции и замыкания :

<?php

class Foo
{
    private $bar = 1;
}

$getBar = function() { return $this->bar; };

$foo = new Foo;
$foo->getBar = $getBar->bindTo($foo, $foo);

echo call_user_func($foo->getBar); // prints "1"

См. https://wiki.php.net/rfc/closures/object-extension для обсуждения реализации и возможностей Closure.gotchas.

В общем, если вы обнаружите, что в вашем классе много длинных методов, попробуйте разбить их на более мелкие куски.Опишите, что делают методы на простом английском языке.Для каждого "и" создайте новый метод и переместите туда код.

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

Я также несколько сомневаюсь в ваших мотивах желания "ленивой загрузки" методов в класс.Нет проблем с производительностью, даже если вы ими не пользуетесь.Если это для оптимизации производительности, вы, вероятно, подходите к ней не с того конца.

Другой вариант - использовать Черты или, что еще проще, Композиция .

4 голосов
/ 22 июня 2011

Решения обратного вызова выглядят очень некрасиво.
Вы можете использовать Composition pattern и автозагрузку:

class MyClass
{

protected $logger;

function SomeFunction($arg)
{
    $this->Logger()->write($arg);
}

function Logger()
{
    if (empty($this->logger)) $this->Logger = new Logger(); //lazy initialization of method
    return $this->logger;
}

}

Но я должен сказать, что все это просто микрооптимизации, не тратьте свое время. Время создания нового объекта и автозагрузки другого файла (использующего диск) будет больше, чем простая инициализация объекта «большими» методами.

1 голос
/ 22 июня 2011
<?php
class BigClass
{
  public function lazy()
  {
    return include 'lazy.func.php';
  }
}

Это должно работать и удовлетворить ваши требования. Я не особо задумывался о возможных побочных эффектах.

1 голос
/ 22 июня 2011

Ваш класс, вероятно, пытается сделать слишком много. Я бы предложил попробовать разделить его на отдельные сервисы. Затем вы можете использовать контейнер внедрения зависимости (например, Pimple ), чтобы лениво загружать только те службы, которые действительно используются.

Я бы посоветовал не злоупотреблять наследством. Вместо этого вы должны отдавать предпочтение композиции вместо наследования . Это делает ваш дизайн чище, а вашу базу кода более удобной для обслуживания.

1 голос
/ 22 июня 2011

Мне неизвестен (эффективный) способ загрузки только частей класса.

Я думаю, вам нужно разделить методы класса на подклассы и использовать автозагрузку для их загрузки.

Как только вы это сделаете, вы сможете подумать о том, чтобы сделать что-то вроде этого:

class myMainClass
 {

   function bigFatMethod($argument, $argument2)
    {
      return mySubClass::bigFatMethod($this, $argument, $argument2); 
      // (pass $this if necessary)
    }
 }  

Это будет bigFatMethod() вызываться внутри myMainClass, но внутри, поскольку вы используете автозагрузку, необходимый код загружается только тогда, когда bigFatMethod() действительно вызывается.

Очевидно, вам нужно будет переписать bigFatMethod(), чтобы он мог вызываться статически, и вместо доступа к $this, вам нужно было бы заставить его обращаться к объекту, переданному в его первом параметре (которому вы передаете $this в родительский класс).

Я никогда не делал этого сам - я хотел бы разделить класс на подклассы и обращаться к ним отдельно - но я не вижу никаких огромных недостатков в таких действиях.

Если вы хотите, вы можете даже абстрагировать bigFatMethod(), используя магический метод __call(), который будет искать, какой подкласс он должен загрузить, выполняет метод и возвращает результат.

0 голосов
/ 22 июня 2011

Вы можете использовать метод магического класса __get для динамической загрузки свойств или магический метод __call для делегирования другим классам / методам. Это замечательно, когда вам нужно инициализировать свойства или методы и т. Д. Только при обращении к ним.

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

http://www.kalekold.net/index.php?post=16

http://php.net/manual/en/language.oop5.magic.php

0 голосов
/ 22 июня 2011

Вы можете написать базовый класс, который охватывает все основные функции, а затем использовать наследование для постепенного добавления более специализированных функций и использовать наследующие классы только там, где это необходимо.

Наследование объектов PHP .

0 голосов
/ 22 июня 2011

загрузить только часть класса - это просто невозможно.Когда вы создаете новый экземпляр класса, он инициализирует весь класс и, следовательно, нет отложенной загрузки функции, вам нужно только

...