Your comments

Дякую за пропозицію! Додам в TODO.

Hi!

Yes, it should work just fine. I'm not sure what the problem might be.

Yurii, the patterns are certainly not part of the math domain—sadly, none of their definitions or features are set in stone, 100% provable, etc. The patterns weren't created from scratch; rather they have been discovered. The original GoF authors over the course of their careers observed 100 codebases with similar code, and they got the idea that in these 100 codebases programmers were driven by the same problem and produced more or less the same code. So, they thought maybe it would save time for any future programmer if we name such a problem and such a solution to this problem "an Adapter". In reality, of those 100 codebases, 90 may have been straightforward adapters, and the other 10 might have been something in between Adapter or Bridge. So now, you can pick and choose what pattern suits your needs, but if there's none, you can simply implement something on your own without using any pattern or create some sort of hybrid of multiple patterns and that would be totally okay. It would be just slightly harder to implement and explain to anyone else.

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.