Цитатите са от известната книга „Design Patterns: Elements of Reusable Object-Oriented Software“
Object types and interfaces
A type is a name used to denote a particular interface. We speak of an object as having the type „Window“ if it accepts all requests for the operations defined in the interface named „Window.“ An object may have many types, and widely different objects can share a type. Part of an object’s interface may be characterized by one type, and other parts by other types. Two objects of the same type need only share parts of their interfaces. Interfaces can contain other interfaces as subsets. We say that a type is a subtype of another if its interface contains the interface of its supertype. Often we speak of a subtype inheriting the interface of its supertype.
Interfaces are fundamental in object-oriented systems. Objects are known only through their interfaces. There is no way to know anything about an object or to ask it to do anything without going through its interface. An object’s interface says nothing about its implementation – different objects are free to implement requests differently. That means two objects having completely different implementations can have identical interfaces.
Видиш ли интерфейсът на даден клас, виждаш какво той може да прави, видиш ли самият клас (имплементация), виждаш как го прави.
Dynamic binding
When a request is sent to an object (calling some of its methods), the particular operation that’s performed, depends on both the request and the receiving object. Different objects that support identical requests may have different implementations of the operations that fulfill these requests (или казано иначе, имплементират по различен начин различни интерфейси). The run-time association of a request to an object and one of its operations is known as dynamic binding.
Dynamic binding means that issuing a request (calling а object method) doesn’t commit you to a particular implementation until run-time. Consequently, you can write programs that expect an object with a particular interface, knowing that any object that has the correct interface will accept the request.
Polymorphism
Dynamic binding lets you substitute objects that have identical interfaces for other, at run-time. This substitutability is known as polymorphism, and it’s a key concept in object-oriented systems.
Kакво е т.н. „интерфейс“?
Какво даден клас може да прави чрез методите си?
Да де, но не само. Интерфейс MagareInterface задължава имплементиращите го класове да могат да ревът и да хвърлят къчове.
Но идеята е интерфейс MagareInterface да задължи дадените методи на имплементиращи даденият интерфейс, да имат зададеният от интерфейсът сигнъчър, демек, да се знае какво се иска като типове параметри, както и като тип върнат резултат.
Classes that aren’t abstract are called concrete classes.
Object’s class vs. Object’s type
It’s important to understand the difference between an object’s class and its type. An object’s class defines how the object is implemented. The class defines the object’s internal state and the implementation of its operations. In contrast, an object’s type only refers to its interface – the set of requests to which it can respond. An object can have many types, and objects of different classes can have the same type.
Демек, класът на даден обект представлява самата му дефиниция. Това е все едно конституцията и законите на една държава, според които тя функционира.
Типът на даденият обект задава неговият интерфейс, тоест как той (обектът) комуникира с останалият свят, тоест какви заявки може да приема и обслужва. Типът е така да се каже „външната политика“ на гореспоменатата държава.
„… and objects of different classes can have the same type.“ – много държави могат да имат обща външна политика най-общо казано, като например членство в едни и същи организации и т.н…
Разбира се, двете понятия за близко свързани помежду си.
Of course, there’s a close relationship between class and type. Because a class defines the operations an object can perform, it also defines the object’s type. When we say that an object is an instance of a class, we imply that the object supports the interface defined by the class.
Демек, понеже класът задава конституцията, то индиректно той задава и външната политика. И щом кажем, че обектът е от клас ЕдикойСи (демек има конституция ЕдикойСи), това по подразбиране значи, че той има и съответната външна политика.
Като пишеш един клас, винаги мислиш първо „какво трябва да може да прави“, и тогава, на тази база пишеш конкретната имплементация. Демек, класът задава конституцията но според това какъв трябва да е типът – външната политика. Явно това се има предвид с принципът „Program to an interface, not an implementation.“
Inheritance vs. Composition
The two most common techniques for reusing functionality in object-oriented systems are class inheritance and object composition. Subclassing is often referred to as white-box reuse. The term „white-box“ refers to visibility.
Демек, класът който наследяваш е напълно известен за теб, като негов наследник, затова явно се има предвид, че е „бяла кутия“. Наследяваш всичко, без разбира се private’s.
Object composition is an alternative to class inheritance. Here, new functionality is obtained by assembling or composing objects to get more complex functionality. This style of reuse is called black-box reuse, because no internal details of objects are visible. Objects appear only as „black boxes.“
За разлика наследяването, тук другите обекти биват „инджектнати“ като пропъртита. Които обекти могат и да не са ни известни като структура и т.н., дадени са ни да им използваме public пропъртитата и методите, демек знаем за тях само толкова, колкото трябва да знаем. Те за нас са като „черна кутия“.
Inheritance and composition each have their advantages and disadvantages. Class inheritance is defined statically at compile-time and is straightforward to use, since it’s supported directly by the programming language. Class inheritance also makes it easier to modify the implementation being reused.
Демек, така може по-лесно да се променя функционалността на наследяваният клас. Как? Чрез overriding.
But class inheritance has some disadvantages too. First, you can’t change the implementations inherited from parent classes at run-time, because inheritance is defined at compile-time. But you can in compile time.
Second, and generally worse, parent classes often define at least part of their subclasses physical representation. Because inheritance exposes a subclass to details of its parent’s implementation, it’s often said that „inheritance breaks encapsulation“. The implementation of a subclass becomes so bound up with the implementation of its parent class that any change in the parent’s implementation will force the subclass to change.
Демек, ако прекаляваме с наследяването, може да достигнем до огромна и тежка „пирамида“ от класове, губейки капсулираност и гъвкавост.
Your classes and class hierarchies will remain small and will be less likely to grow into unmanageable monsters.
Object composition is defined dynamically at run-time through objects acquiring references to other objects. Composition requires objects to respect each others’ interfaces, which in turn requires carefully designed interfaces that don’t stop you from using one object with many others. But there is a payoff. Because objects are accessed solely through their interfaces, we don’t break encapsulation. Any object can be replaced at run-time by another as long as it has the same type. Moreover, because an object’s implementation will be written in terms of object interfaces, there are substantially fewer implementation dependencies.
Демек, трудността тук е да използваме инджектнатите обекти правилно, според интерфейсите им, тоест и самите интерфейси трябва да са добре направени. Предимството на composition е, че не се нарушава капсулацията, всеки обект е само за себе си, и най-вече – взаимозаменяемостта им на база че са от общ тип (имат общ интерфейс). Демек, не „разширяваш“ даден клас чрез наследяване, а подаваш готов обект от даденият клас, на друг обект.
That leads us to our second principle of object-oriented design: Favor object composition over class inheritance.
Delegation
Това е принцип, който използва composition, на практика постига същият ефект като inheritance но го прави не като наследява даден клас и използва неговите методи и пропъртита с this или self, а получава готов обект като параметър и използва неговите такива.
Relating Run-Time and Compile-Time Structures
An object-oriented program is a run-time structure, that often bears little resemblance to its code structure. The code structure is frozen at compile-time; it consists of classes in fixed inheritance relationships. A program’s run-time structure consists of rapidly changing networks of communicating objects. In fact, the two structures are largely independent. Trying to understand one from the other is like trying to understand the dynamism of living ecosystems from the static taxonomy of plants and animals, and vice versa.
Демек, структурата от класове е все едно енциклопедията с растения и животни, но самата програма е вече съвкупност от динамични обекти, които комуникират помежду си, зависят един от друг…, и това е все едно една жива екосистема, една гора например.
A design that doesn’t take change into account risks major redesign in the future.