Як слід додати функціональність об'єкту, який вже існує?


25

У мене є інтерфейс, який має певну кількість чітко визначених функціональних можливостей. Скажімо:

interface BakeryInterface {
  public function createCookies();
  public function createIceCream();
}

Це добре працює для більшості реалізацій інтерфейсу, але в декількох випадках мені потрібно додати деякі нові функціональні можливості (наприклад, можливо, перекладені на новий метод createBrownies()). Очевидним / наївним підходом до цього було б розширення інтерфейсу:

interface BrownieBakeryInterface extends BakeryInterface {
  public function createBrownies();
}

Але є досить великий мінус у тому, що я не можу додати нову функціональність без зміни існуючого API (наприклад, зміни класу на використання нового інтерфейсу).

Я думав про використання адаптера, щоб додати функціональність після інстанції:

class BrownieAdapter {
  private brownieBakery;

  public function construct(BakeryInterface bakery) {
    this->brownieBakery = bakery;
  }

  public function createBrownies() {
    /* ... */
  }
}

Що б мені чистило щось на кшталт:

bakery = new Bakery();
bakery = new BrownieBakery(bakery);
bakery->createBrownies();

Це здається гарним рішенням проблеми, але мені цікаво, чи пробуджую я старих богів, роблячи це. Чи потрібний адаптер? Чи є краща модель, яку слід дотримуватися? Або я повинен просто просто кусати кулю і просто розширювати оригінальний інтерфейс?


У Delphi є допоміжні класи, це як додавання методів до існуючих класів, не змінюючи їх. Наприклад, у Delphi є клас TBitmap, визначений у його графічному блоці, ви можете створити допоміжний клас, який додає, скажімо, функцію Flip до TBitmap. Поки клас хелперів знаходиться в обсязі, ви можете зателефонувати на MyBitmap.Flip;
Білл

Відповіді:


14

Будь-який з шаблонів ручки може відповідати опису, залежно від ваших точних вимог, мови та необхідного рівня абстракції.

Пуристичний підхід був би шаблоном « Декоратор» , який робить саме те, що ви шукаєте, динамічно додає відповідальності перед об’єктами. Якщо ви насправді будуєте пекарні, це, безумовно, зайве, і вам слід просто перейти на адаптер.


Це саме те, що мені було потрібно: я зрозумів, що за допомогою адаптера викручується залежність від введення залежності, але за допомогою декоратора це обходиться.

5

Дослідіть концепцію горизонтального повторного використання , де ви можете знайти такі речі, як « Риси» , досі експериментальне, але вже доказове виробництво, орієнтоване на аспекти програмування та іноді ненависні Mixins .

Прямий спосіб додавання методів до класу також залежить від мови програмування. Ruby дозволяє проводити виправлення мавп, тоді як спадщина на основі прототипу Javascript , де класи насправді не існують, ви створюєте об'єкт і просто копіюєте його та продовжуєте додавати його, наприклад:

var MyClass = {
    do : function(){...}
};

var MyNewClass = new MyClass;
MyClass.undo = function(){...};


var my_new_object = new MyNewClass;
my_new_object.do();
my_new_object.undo();

Нарешті, ви також можете імітувати горизонтальне повторне використання або "модифікацію" та "доповнення" поведінки класу / об'єкта за допомогою відображення .


4

Якщо є вимога, що bakeryекземпляр повинен динамічно змінювати свою поведінку (залежно від дій користувача тощо), тоді слід перейти до шаблону « Декоратор» .

Якщо bakeryдинамічно не змінюється поведінка, але ви не можете змінити Bakery class(зовнішній API тощо), слід перейти до шаблону адаптера .

Якщо bakeryдинамічно не змінюється його поведінка і ви можете змінити, Bakery classто слід розширити існуючий інтерфейс (як ви спочатку пропонували) або ввести новий інтерфейс BrownieInterface і дозволити Bakeryреалізувати два інтерфейси BakeryInterfaceта BrownieInterface.
Інакше ви без зайвих причин додасте зайвий складність у свій код (використовуючи візерунок «Декоратор»)!


2

Ви зіткнулися з проблемою вираження. У цій статті Стюарта Сьєрра обговорюється рішення клоджура, але він також наводить приклад на Java та перераховує популярні рішення в класичному OO.

Існує також презентація Кріса Хоузера, яка обговорює майже таку саму основу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.