Super-шаблон MFШаблоны MaxSite CMSНТML-курсы бесплатноРеклама на сайтеTelegram-канал
MaxSite.org

Как создать свой сайт

Заказать сайт/блог/лендинг недорого
Дневник Софт PHP CSS, HTML, LESS, SASS PHP/ООП SEO Шаблон MF Alpine.js Тайм-менеджмент jQuery и JavaScript Java и Android CodeIgniter Landing Page Git. GitHub
PHP/ООП
  • Паттерн Singleton в PHP
  • ООП в PHP
  • Композиция и наследование ООП в PHP
  • Шаблоны проектирования для PHP
  • Шаблон «Fluent interface» (Текучий интерфейс)
  • Шаблон проектирования Facade (Фасад)
  • Шаблон Abstract Factory (Абстрактная фабрика) на PHP
  • Шаблон Factory Method (Фабричный метод) на PHP
  • Шаблон Singleton. Трейты (trait) в PHP
  • Шаблон Multiton (Пул одиночек)
  • Шаблон Registry (Реестр)
  • Шаблон Composite (Компоновщик)
  • Шаблон Builder (Строитель)
  • Шаблон Strategy (Стратегия)
  • Шаблон проектирования Adapter (адаптер)
  • Шаблон Observer (Наблюдатель)
  • Шаблон Prototype (Прототип)
  • Шаблон Bridge (Мост)
  • Шаблон Decorator (Декоратор)
  • Шаблон Flyweight (Приспособленец)
  • Шаблон Proxy (Заместитель)
  • Шаблон «Template method» (Шаблонный метод)
  • Dependency injection (внедрение зависимости)

Я готов сделать для вас сайт/лендинг на MaxSite CMS с установкой, настройкой, созданием шаблона, подгонкой дизайна и т.д. У меня большой опыт и множество созданных сайтов, плагинов, шаблонов. Также я выполняю отдельные работы по PHP и HTML5/CSS3-верстке. Могу поучаствовать в вашем стартапе как php-программист. При необходимости переведу ваш сайт с другого «движка» (WP, Wix и т.д.) на MaxSite CMS, чтобы он стал быстро работать.

Принимаю заказы на создание telegram-ботов.

Несколько примеров последних работ:

  • b-reading.ru
  • dailycars.com.ua
  • vcredite.com.ua
  • cq-herson.info
  • eremeev.org
  • booksread.ru
  • beregite-zdorovje.ru
  • regression.pro

Подробнее о моих услугах

Композиция и наследование ООП в PHP

PHP/ООПООП5 июня 2019 г.

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

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

Есть даже такое выражение «Композиция лучше наследования», которое хорошо известно программистам Java или С++. Попробуем посмотреть на эти вопросы с точки зрения PHP.

Для начала немного базовых знаний. Наследование — это когда один класс расширяет другой.

class A 
{
	public function run()
	{
		echo 'run';
	}
}
 
class B extends A 
{
	public function stop()
	{
		echo 'stop';
	}
}
 
$b = new B;
$b->run();  // run
$b->stop(); // stop

Это пример типового наследования, когда класс B расширяет функционал класса A. При этом класс B получает функциональность класса A.

Важный момент здесь в том, что мы практически ничего не сделали, только с помощью «extends» указали связь, но при этом нам не нужно было инстанцировать родительский класс: PHP всю работу выполнил самостоятельно.

Теперь попробуем усложнить задачу. Пусть нам нужно вывести в браузер части страницы: head и footer. Получится такой класс:

class Print 
{
	public function go()
	{
		$this->head();
		$this->footer();
	}
	public function head()
	{
		echo 'HEAD';
	}
	
	public function footer()
	{
		echo 'FOOTER';
	}
}
 
$print = new Print();
$print->go(); // HEAD FOOTER

Теперь нам нужно добавить ещё одну часть, например body. Простой вариант — расширить класс.

class PrintA extends Print
{
	public function body()
	{
		echo 'BODY';
	}
}
 
$print = new PrintA();
$print->go();   // HEAD FOOTER
$print->body(); // BODY

Всё просто, но что делать, если нужно body вывести между head и footer? Очевидно, что единственным способом будет переопределить и метод go().

class PrintA extends Print
{
	public function go()
	{
		$this->head();
		$this->body();
		$this->footer();
	}
	
	public function body()
	{
		echo 'BODY';
	}
}
 
$print = new PrintA();
$print->go(); // HEAD BODY FOOTER

В данном примере нарушен один из базовых принципов SOLID, а именно «Принцип подстановки Барбары Лиско»: наследующий класс должен дополнять, а не изменять базовый. В нашем случае мы переопределили метод родительского класса и теперь вообще не ясно зачем он там нужен. Если мы усложним задачу, и введём еще десяток условий, то получится довольно длинная цепочка классов, которые перекрывают друг друга самым произвольным образом. Работать с таким кодом будет проблематично.

Примеры кода утрированы, чтобы упростить их понимание.

Попробуем решить задачу с другой стороны. Пусть у head, footer и body будут свои классы, а сам вывод сделаем с помощью отдельного класса.

class PrintHead 
{
	public function print()
	{
		echo 'HEAD';
	}
}
 
class PrintFooter
{
	public function print()
	{
		echo 'FOOTER';
	}
}
 
class PrintBody
{
	public function print()
	{
		echo 'BODY';
	}
}
 
class Print
{
	public function go()
	{
		$head = new PrintHead();
		$body = new PrintBody();
		$footer = new PrintFooter();
		
		$head->print();
		$body->print();
		$footer->print();
	}
}
 
$print = new Print();
$print->go(); // HEAD BODY FOOTER

Такой подход и называется композицией (или «агрегирование по значению»). Здесь тонкость в том, что класс Print сам инстанцирует все нужные классы. После того как Print будет уничтожен, будут уничтожены и все созданные им объекты.

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

Например в классах PrintHead, PrintBody и PrintFooter используется одноименный метод print(). Если стоит задача добавить какой-то новый вариант, то нужно будет следовать этой же схеме. Но, представим себе, что какой-то программист решил использовать другой метод, например out(). В этом случае при создании класса Print волей-неволей придётся учитывать эту особенность.

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

interface PrintInterface
{
    public function print();
}
 
class PrintHead implements PrintInterface
 
class PrintFooter implements PrintInterface
 
class PrintBody implements PrintInterface

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

class Print
{
	public function go($h, $b, $f)
	{		
		$h->print();
		$b->print();
		$f->print();
	}
}
$head = new PrintHead();
$body = new PrintBody();
$footer = new PrintFooter();
 
$print = new Print();
$print->go($head, $body, $footer); // HEAD BODY FOOTER

После того, как объект Print будет уничтожен, остальные останутся и будут доступны для использования. При агрегации также можно перенести логику выполнения из исполняемого класса Print: скажем поменять местами head и footer в параметрах.

Композиция и агрегация являются основой для многих шаблонов проектирования. Покажу для примера порождающий шаблон «Фабричный метод» (второе название «Виртуальный конструктор»).

abstract class CommonAbstract
{
    public static function initial($class)
    {
        return new $class();
    }
	
    abstract public function run();
}
 
class Class1 extends CommonAbstract
{
    public function run()
    {
        echo 'Class1 run';
    }
}
 
class Class2 extends CommonAbstract
{
    public function run()
    {
        echo 'Class2 run';
    }
}
 
// demo
 
$a = CommonAbstract::initial('Class1');
$a->run(); // Class1 run
 
$b = CommonAbstract::initial('Class2');
$b->run(); // Class2 run

Каждый класс расширяет абстрактный CommonAbstract, где используется статический метод initial(), через который инстанцируется нужный класс. Абстрактный класс задаёт единый тип поведения (это наследование), но при этом создаётся новый нужный объект (композиция). Такая архитектура позволяет без труда добавить новый класс без переделки остальных.

В других шаблонах проектирования используются другие алгоритмы, но все они также базируются на основных принципах ООП: наследование, композиция и агрегация.

Шаблоны проектирования для PHP
ООП в PHP
twitter.com facebook.com

Другие записи сайта

  • Работы по «MF». Подвал
  • Небольшое интервью
  • MaxSite CMS один год!
  • Отличия MaxSite CMS от WordPress с точки зрения верстки шаблона
  • Создание telegram-бота
  • Тук, стук! Откройте, Вконтакте!
  • Делаем резервные копии с помощью xcopy

Комментариев: 4 RSS

1Андрей31-08-2020 14:52

В данном примере нарушен один из базовых принципов SOLID, а именно «Принцип подстановки Барбары Лиско»: наследующий класс должен дополнять, а не изменять базовый.

Может быть принцип открытости/закрытости?

2MAX02-09-2020 08:54

Нет, здесь именно нарушен принцип Лискова.

3Zhenya02-10-2020 06:47

А чем это плохо, что нарушен?

4MAX02-10-2020 09:07

Если в классе PrintA вы заменяете методы из родительского класса Print, то зачем вообще они в Print созданы?

Оставьте комментарий!

Комментарий будет опубликован после проверки. Вы соглашаетесь с правилами сайта.

(обязательно)

MaxSite.org
Блог о том, как создавать сайты

Услуги по созданию сайтов, блогов, лендингов
Обратная связь • Реклама на сайте
Мои проекты
  • MaxSite CMS
  • Super-шаблон MF
  • Шаблоны MaxSite CMS
  • Berry CSS (CSS Utilities)
  • Albireo Framework
  • UniCSS (Universal Atomic CSS)
  • Landing Page Framework
  • Бесплатные НТML-курсы
Ссылки
  • Telegram-канал
  • Github
  • Twitter
  • Telegram-бот
  • RSS
© MaxSite.org, 2006-2021. Работает на MaxSite CMS / Время: 0.2776 / SQL: 25 / Память: 1.38MB / Вход