Your comments

Hi Yurii!

They look similar to you because you think of them only in terms of implementation. They both are based on the composition principle, so they do look really close if you strip all the context and rename the classes to A, B, C. In fact, if you do the same with Strategy and some others, it'll also look pretty similar.

However, this is not the right way to think of them. A pattern defines not just some piece of code, but also an underlying problem. You don't recommend using Bridge when someone struggles with converting XML to JSON. Similarly, you don't recommend using Adapter, when people are about to write an abstraction layer over operating systems. The ability to communicate a complex idea using one name is an integral part of having these patterns.

There are other aspects as well, such as potential future vectors in which the classes can evolve in different patterns. When using Adapter, you usually assume that the thing that you adapt from isn't going to change much (hell, you might not have control over that code at all). However, the abstraction part of Bridge may be very actively developed.

Hope this helps!

Hi!

Suppose you have a Bridge hierarchy like this:

Abstraction: BasicFileSystem → VersionControlFileSystem

Implementations: Fat, NTFS, APFS

One of the abstractions (VersionControlFileSystem) can only work with a specific implementation (NTFS, APFS).

Instead of requiring client to know about this implication when instantiating Abstractions & Implementations, you could provide abstract factory to create necessary classes (and maybe even handle errors when non-existent pairs are requested). Below is a pseudocode:

interface FileSystemFactory {
createBasicFileSystem() → BasicFileSystem
createVersionControlFileSystem() → VersionControlFileSystem
}

class WindowsFileSystemFactory {
createBasicFileSystem() → BasicFileSystem + Fat
createVersionControlFileSystem() → VersionControlFileSystem + NTFS
}

class macOsFileSystemFactory {
createBasicFileSystem() → BasicFileSystem + Fat
createVersionControlFileSystem() → VersionControlFileSystem + APFS
}

Hi!

Thanks for pointing it out. I think I needed to reword that item.

What I meant is that you don't need a class hierarchy to implement a prototype. You can have a working prototype right in the base class, so no inheritance is really necessary (if you don't have subclasses).

On the other hand, the factory method doesn't really make sense without a class hierarchy.

Здраствуйте!

Спасибо за вопрос! Хочу подчеркнуть, что именно следование этому принципу обеспечивает расширяемость и гибкость программного обеспечения, позволяет избежать дублирования кода и улучшает повторное использование кода. Такое следование не получается само-собой автоматически при наследовании.

Например, один из шагов алгоритма подразумевает, скажем, что вы сохраняете файл (этим вы задаёте неявный инвариант метода - наличие нового файла - это один из пунктов в определении принципа Лисков), а следующий шаг с этим файлом что-то делает. Затем вы отнаследовали первый метод и убрали сохранение файла, а значит нарушили инвариант метода, а значит и LSP (ну и, вероятно, сломали работу класса).

Hi!

Sorry for the confusion. I mean that all the subclasses already implement it implicitly if the superclass implements it explicitly. In other words, you can't refuse to have some parts of the original interface in a subclass, it already implements it and you can't change that.

I'm glad that my work was helpful to you!

Thank you for the kind words!