Design Patterns: Elements of Reusable Object-Oriented Software
31
You have to instantiate concrete classes (that is, specify a particular
implementation) somewhere in your system, of course, and the creational patterns
(Abstract Factory (99), Builder (110), Factory Method (121), Prototype (133),
and Singleton (144) let you do just that. By abstracting the process of object
creation, these patterns give you different ways to associate an interface with
its implementation transparently at instantiation. Creational patterns ensure
that your system is written in terms of interfaces, not implementations.
Putting Reuse Mechanisms to Work
Most people can understand concepts like objects, interfaces, classes, and
inheritance. The challenge lies in applying them to build flexible, reusable
software, and design patterns can show you how.
Inheritance versus Composition
The two most common techniques for reusing functionality in object-oriented
systems are class inheritance and object composition. As we've explained, class
inheritance lets you define the implementation of one class in terms of another's.
Reuse by subclassing is often referred to as white-box reuse. The term "white-box"
refers to visibility: With inheritance, the internals of parent classes are often
visible to subclasses.
Object composition is an alternative to class inheritance. Here, new functionality
is obtained by assembling or
composing
objects to get more complex functionality.
Object composition requires that the objects being composed have well-defined
interfaces. This style of reuse is called black-box reuse, because no internal
details of objects are visible. Objects appear only as "black boxes."
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. When a subclass
overrides some but not all operations, it can affect the operations it inherits
as well, assuming they call the overridden operations.
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. 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" [Sny86]. 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.
Do'stlaringiz bilan baham: |