Home Random Page


CATEGORIES:

BiologyChemistryConstructionCultureEcologyEconomyElectronicsFinanceGeographyHistoryInformaticsLawMathematicsMechanicsMedicineOtherPedagogyPhilosophyPhysicsPolicyPsychologySociologySportTourism






Nbsp;   Design: Base Class or Interface?

I often hear the question, “Should I design a base type or an interface?” The answer isn’t always clear-

cut. Here are some guidelines that might help you:

 

IS-A vs. CAN-DO relationshipA type can inherit only one implementation. If the derived type can’t claim an IS-A relationship with the base type, don’t use a base type; use an inter- face. Interfaces imply a CAN-DO relationship. If the CAN-DO functionality appears to belong with various object types, use an interface. For example, a type can convert instances of itself


to another type (IConvertible), a type can serialize an instance of itself (ISerializable), etc. Note that value types must be derived from System.ValueType, and therefore, they can- not be derived from an arbitrary base class. In this case, you must use a CAN-DO relationship and define an interface.

Ease of useIt’s generally easier for you as a developer to define a new type derived from a base type than to implement all of the methods of an interface. The base type can provide a lot of functionality, so the derived type probably needs only relatively small modifications to its behavior. If you supply an interface, the new type must implement all of the members.

Consistent implementationNo matter how well an interface contract is documented, it’s very unlikely that everyone will implement the contract 100 percent correctly. In fact, COM suffers from this very problem, which is why some COM objects work correctly only with Microsoft Word or with Windows Internet Explorer. By providing a base type with a good default implementation, you start off using a type that works and is well tested; you can then modify parts that need modification.

VersioningIf you add a method to the base type, the derived type inherits the new method, you start off using a type that works, and the user’s source code doesn’t even have to be re- compiled. Adding a new member to an interface forces the inheritor of the interface to change its source code and recompile.

In the FCL, the classes related to streaming data use an implementation inheritance design. The System.IO.Stream class is the abstract base class. It provides a bunch of methods, such as Read and Write. Other classes—System.IO.FileStream, System.IO.MemoryStream, and System.Net.

Sockets.NetworkStream—are derived from Stream. Microsoft chose an IS-A relationship between each of these three classes and the Stream class because it made implementing the concrete classes easier. For example, the derived classes need to implement only synchronous I/O operations; they inherit the ability to perform asynchronous I/O operations from the Stream base class.

Admittedly, choosing to use inheritance for the stream classes isn’t entirely clear-cut; the Stream base class actually provides very little implementation. However, if you consider the Windows Forms control classes, in which Button, CheckBox, ListBox, and all of the other controls are derived from System.Windows.Forms.Control, it’s easy to imagine all of the code that Control implements, which the various control classes simply inherit to function correctly.



By contrast, Microsoft designed the FCL collections to be interface based. The System.Collec­ tions.Generic namespace defines several collection-related interfaces: IEnumerable<out T>, ICollection<T>, IList<T>, and IDictionary<TKey, TValue>. Then Microsoft provided a num- ber of classes, such as List<T>, Dictionary<TKey, TValue>, Queue<T>, Stack<T>, and so on, that implement combinations of these interfaces. Here the designers chose a CAN-DO relationship be- tween the classes and the interfaces because the implementations of these various collection classes are radically different from one another. In other words, there isn’t a lot of sharable code between a List<T>, a Dictionary<TKey, TValue>, and a Queue<T>.


The operations these collection classes offer are, nevertheless, pretty consistent. For example, they all maintain a set of elements that can be enumerated, and they all allow adding and removing of ele- ments. If you have a reference to an object whose type implements the IList<T> interface, you can write code to insert elements, remove elements, and search for an element without having to know exactly what type of collection you’re working with. This is a very powerful mechanism.

Finally, it should be pointed out that you can actually do both: define an interface and provide a base class that implements the interface. For example, the FCL defines the IComparer<in T> inter- face and any type can choose to implement this interface. In addition, the FCL provides an abstract base class, Comparer<T>, which implements this interface and provides a default implementation for the non-generic IComparer’s Compare method. Having both an interface definition and a base class offers great flexibility because developers can now choose whichever they prefer.


 
 

 


PART III

Essential Types

CHAPTER 14............Chars, Strings, and Working with Text317

CHAPTER 15.................Enumerated Types and Bit Flags............ 361

CHAPTER 16..............................................Arrays............ 373

CHAPTER 17.........................................Delegates............ 391

CHAPTER 18.................................Custom Attributes............ 421

CHAPTER 19.............................Nullable Value Types441


C HA P T E R 1 4


Date: 2016-03-03; view: 629


<== previous page | next page ==>
Nbsp;   Be Careful with Explicit Interface Method Implementations | Nbsp;   Characters
doclecture.net - lectures - 2014-2024 year. Copyright infringement or personal data (0.006 sec.)