Проверка на наследяване на характеристики PHP

И така, най-накрая започнах да си играя с черти и те са много удобни, проблемът, който имах е, че искам да имам някои черти, за да добавя функционалност към моите обекти с данни. Само по себе си това е просто, с изключение на това, че при това използвам методи, които са дефинирани в моя обект с основни данни

abstract class Base_Object {
  protected function _addToUpdate($field, $value) {
    ...
  }
  ...
}

trait Extended_Object {
  public function doSomeStuff() {
    ...
    $this->_addToUpdate($someVar, $someOtherVar);
  }
  ...
}

class User extends Base_Object {
  use Extended_Object;
  ...
}

Тогава проблемът ми е, ако някой друг в екипа ми реши да използва чертата Extended_Object върху обект, който не разширява Base_Object. Мислех да поставя отметка в метода _addToUpdate, но в идеалния случай бих искал да се показва грешка при създаването на екземпляра.

Измислих решение, което работи, но ме кара да се чувствам малко мръсен и е далеч от идеалното

abstract class Base_Object {
  protected function _addToUpdate($field, $value) {
    ...
  }
  ...
}

trait Extended_Object {
  abstract protected function _addToUpdate($field, $value);

  public function doSomeStuff() {
    ...
    $this->_addToUpdate($someVar, $someOtherVar);
  }
  ...
}

class User extends Base_Object {
  use Extended_Object;
  ...
}

Чрез добавяне на абстрактен метод към Extended_Object мога поне да съм сигурен, че ще се покаже грешка, ако методът, от който се нуждая в Base_Object, не присъства, но не съм гарантиран, че въпросният метод действително ще направи това, което искам да направи.

В идеалния случай бих искал да мога да стартирам нещо като кода по-долу, когато даден обект се инстанцира с помощта на чертата Extended_Object

if (!($this instanceof Base_Object)) {
  throw new Inheritance_Exception("Base_Object");
}

Надявам се, че някой е намерил начин да направи това или поне по-добро решение от моето.

Забележка: знам, че бих могъл да направя това с конструктор, но това би било жизнеспособно само при използване на една характеристика, ако реша на по-късна дата да създам още няколко черти за разширение на обект, ще стане много объркващо много бързо

Редактиране: осъзнавам, че характеристиките наистина не са предназначени за това, което се опитвам да направя, но те поне отчасти ми позволяват да заобиколя проблема с единичното наследяване и знам, че не съм единственият разработчик, който планира използвайте ги по този начин


person Matt Indeedhat Holmes    schedule 04.06.2014    source източник
comment
Имам отношения на любов и омраза с черти, ако можех да преименувам и двата атрибута, както и методите (може би използвайки една и съща черта повече от веднъж в един и същи клас) и във всеки момент, не само когато има конфликт, тогава черти биха се разтърсили. Въпреки това, дори и да се разглеждат като не повече от полуизпечен опит за „хоризонтално наследяване“ (композиция), те изглежда все още си струва да бъдат използвани.   -  person user5321531    schedule 01.08.2014


Отговори (4)


Е (IMHO) проблемът съществува в начина, по който използвате черти, който технически е анти-модел.

Първо, какво представляват характеристиките и защо да ги използвам: За да цитирам един от коментари в php.net

Най-добрият начин да разберете какво представляват характеристиките и как да ги използвате е да ги разгледате за това, което всъщност представляват: езиково подпомагано копиране и поставяне.

Ако можете да копирате и поставите кода от един клас в друг (и всички сме правили това, въпреки че се опитваме да не го правим, защото кодът се дублира), тогава имате кандидат за черта.

Сега дори тези характеристики ви позволяват да използвате членове на класа, който ги използва, това не означава, че трябва да правите това. обратно към SRP, това е истинско нарушение. PHP ви позволява да правите echo $_POST['XSS data'], но това не означава, че трябва да го правите. Така че, независимо от начина, по който искате да ограничите използването на характеристиките си, това, което всъщност правите е, че представяте проблема и се опитвате да го разрешите, така че, за да отговорите на въпроса си, просто преработете кода си за да не използвате методи и членове на клас въз основа на предположението, че всеки клас, който ще използва чертата, трябва да има тези методи и членове.

person mamdouh alramadan    schedule 04.06.2014

Мисля, че вашето решение с abstract function е точно това, за което е предназначено това съоръжение. Съгласен съм с усещането ви:

не съм гарантиран, че въпросният метод наистина ще направи това, което искам

Това обаче е същото предположение, което правите винаги, когато използвате interface: единственото нещо, което се твърди е, че методът съществува с правилния подпис (което в PHP възлиза най-вече на неговото име и брой параметри). Няма начин да знаем, че функцията действително се държи по особено полезен начин, когато й бъдат дадени тези параметри.

В случай на trait, абстрактен метод на практика е същия вид договор като interface: "за да use това trait, трябва да предоставите тези предварителни условия".

Бих отбелязал също, че общото описание на traits е "автоматично копиране и поставяне", така че обикновено трябва да се разглежда като отделно от йерархиите на обекти. По-технически, той представлява "повторно използване на хоризонтален код", а не "множествено наследяване". Например можете да дефинирате trait, което позволява от class до implement до interface, но за външния код interface е важно, а не trait.

person IMSoP    schedule 04.06.2014

Изправен съм пред подобен проблем, но с множество подкласове, наследени от базовия клас.

В този момент би имало смисъл да поставите абстрактни декларации на черти в някакъв файл за включване и като самата характеристика, напр.:

abstract class Base_Object {

  use BaseObjectTrait;
  ...
}

trait BaseObjectTrait {

  protected function _addToUpdate($field, $value) {
    ...
  }

}

trait BaseObjectTraitPrototypes {

  abstract protected function _addToUpdate($field, $value);

}

trait Extended_Object {

  use BaseObjectTraitPrototypes;

  public function doSomeStuff() {
    ...
    $this->_addToUpdate($someVar, $someOtherVar);
  }
  ...
}

class User extends Base_Object {
  use Extended_Object;
  ...
}

Може ли някой да измисли по-елегантно решение от това? Тук използвам термина „прототип“ много свободно от смисъл на програмиране на C. „Header“ файловете също са от програмен контекст на C, но все още не са идеални. Някой може ли да измисли по-кратка концепция за проблемния домейн, може би нещо приемливо за php-fig?

person user5321531    schedule 01.08.2014
comment
Отговаряйки на собствения си въпрос тук, какво ще кажете за добавяне на наставката „Интерфейс“ към чертата, за да наименувате характеристика, която декларира абстрактни дефиниции на характеристиките на черта, като абстрактните декларации след това се включват в черти на подкласове (напр. в горния пример BaseObjectTraitPrototypes ще стане BaseObjectTraitInterface). Като решение все още не изглежда 100% правилно, но мисля, че се движи в правилната посока. Автоматичното генериране на интерфейса от IDE би било бонус. - person user5321531; 01.08.2014
comment
Като алтернатива BaseObjectPrototype - прототипен файл, може би имплементиран като характеристика (use ...) или евентуално като ясен include ... файл, деклариращ прототипи не само за всеки файл с характеристики, свързан с класа, но също и всички методи и свойства в самия клас. Директният include ... файл би бил по-семантично правилен от гледна точка на самия PHP език. - person user5321531; 02.08.2014
comment
но с множество подкласове, наследени от базовия клас. - просто съм любопитен относно вашия проблем... предполагате ли, че вашата черта е извикването на методи от конкретен подклас? И след това се опитвате да използвате тази черта в други (хоризонтални) подкласове? - person MrWhite; 18.05.2016

Тогава проблемът ми е, ако някой друг в екипа ми реши да използва чертата Extended_Object върху обект, който не разширява Base_Object.

:

В идеалния случай бих искал да мога да стартирам нещо като кода по-долу, когато даден обект се инстанцира с помощта на характеристиката Extended_Object

if (!($this instanceof Base_Object)) {
    throw new Inheritance_Exception("Base_Object");
}

Може би пропускам нещо, но ако тази черта трябва някога да се прилага само към екземпляри на Base_Object, тогава звучи така, сякаш тази „характеристика“ трябва да бъде подклас на Base_Object, а не черта изобщо?

След това класът User разширява този Extended_Object (подклас), вместо да useвключва Extended_Object (характеристика).

От: http://php.net/manual/en/language.oop5.traits.php

Характеристиката има за цел да намали някои ограничения на единичното наследяване, като позволи на разработчика да повторно използва набори от методи свободно в няколко независими класа, живеещи в различни йерархии на класове.

person MrWhite    schedule 18.05.2016