Да си представим проста клас-структура от например абсклас и неговите наследници, която да е йерархична и да пресъздава например продуктите в хипермаркет за електроника.
Имаме например клас ProductAbstract, който ще има няколко подкласа, например: BlackElectronics, Kitchen, PC, Smartphones…
Под BlackElectronics можем да имаме например: TV, HiFi…
Под TV – например телевизори Panasonic, Sony… и т.н…
Aко например искаме за всеки продукт да имаме 3 цени в три валути, един начин е под всеки от горните класове да имаме по 3 класа за всяка от трите валути, напр: PanasonicBGN, PanasonicEUR, PanasonicUSD, SonyBGN, SonyEUR, SonyUSD…
(Знам, че примерът не е добър, не обръщайте внимание, нека схванем идеята.)
Веднага се вижда, че по този начин ще имаме Декартово произведение от класове, щом комбинираме всеки с всеки.
Отделно, ако ни трябва нова валута?
Пиши народе възродени още N на брой класове за телевизори, перални…
Няма ли да е по-добре да имаме отделно три класа например, за отделните валути – PriceBGN, PriceEUR и PriceUSD, и за да са заменяеми – да имплементират общ интерфейс, например PricesInterface?
И в първата структура (с продуктите) ще имаме едно protected property например $priceObj, където ще е текущият обект за валута, която ни трябва за да сметнем цената на продукта. Демек, той ще има например метод getCurrencyCourse()
Така, вместо за всеки клас Panasonic да имаме примерно 3 подкласа за трите валути, ще имаме само Panasonic, а от това кой от трите класа за валути имаме в $priceObj, имаме и валутният курс, който ни трябва да си сметнем цената.
Eто това е идеята на Bridge design pattern – да раздели отделните структури от класове, отделните абстракции отделно, за да могат да се развиват независимо и това да не предизвика експлозия от класове като Декартово произведение.
Демек, този design pattern е идеален в случаите когато дадена структура от абстрактни класове и подкласове може да стане твърде сложна, с много повторения на код, да експлоадира от класове, и не на последно място – малка промяна (нов абсметод в родителски клас – абсклас например) да наложи същата промяна и в подкласовете. Последното например, ще наруши Interface segregation principle.
Между другото, важно е да се знае, че двете абстрактни структури, за които говорим могат да са напълно независими една от друга.
Също и е важно да се знае, че този „мост“ между така да се каже „основната“ абсструктура (за продуктите) и така да се каже „помощната“ (за валутите)… може да имаме много такива „мостове“, и към други „помощни“ абсструктури“…
Например ако искаме да имаме два изгледа на страниците за всеки продукт – short (с малко инфо) и long (с пълната информация), можем да имаме още една абсструктура ViewInterface с два подкласа ShortView и LongView. И съответно, и за тази абсструктура да има едно protected property $view.
И не на последно място, примерът с цените на продуктите по-горе, е само за едно свойство на продукта, само за пример. По принцип така трябва да се направи за всички пропъртита на продуктите. Демек, в абсструктурата ProductAbstract трябва да остане само структурата на продуктите, демек, йерархичните зависимости кой под кого е и т.н…
Самите неща, специфични за отделните продукти, трябва да се инджектват (бриджват) от други абсструктури, подобно на цените.
Да разгледаме един друг пример
Имаме абсструктура BaseRepository, чиято идея е да може да съдържа подкласове за например студенти (StudentRepository) и т.н..
И съответно StudentRepository също ще имам например два подкласа SaveStudentInFile и SaveStudentInDB със своите съответни методи за например различни валидирания, проверки и т.н…, и също и методи за запазване на информацията във файлове или в база данни.
Вижда се веднага, че първо имаме повторение на методи и уникалната част е само тази за запазването на информацията.
Второ – при нужда от трети, четвърти… начин на запазване, това значи още подобни класове, и още повторение…
Не е ли по-елегантно:
1) да оставим в тази сегашна абсструктура само нещата, свързани само със студентите – проверки, валидации и т.н…
2) да създадем втора, паралелна абсструктура например StorageRepository, която ще има например два подкласа FileStorage и DBStorage, които освен специфичните си методи ще имат и един публичен например save().
3) обект от StorageRepository ще бъде инджекват в обект от Students, например през констуктора, и ще бъде използван за запазване на информацията за студентите, чрез извикване на метода save().
Eто как като разделихме двете абсструктури, можем да ги развиваме напълно независимо една от друга. Можем да имаме както не само студенти, но и да ги запазваме както си искаме, без да пишем по един клас за всеки вариант – SaveStudentInFile, SaveStudentInDB, SaveStudentInWhatever…, SaveProfessorInFile и SaveProfessorInDB…
Литература: