Tuesday, July 6, 2010

When To Use Abstract Classes

Introduction

Up until recently, my C# designs never started off containing abstract classes. Things always started their lives as either concrete classes or as interfaces. Occasionally an abstract method would appear in a class but that was the exception.

I had read that abstract classes weather change better than interfaces but that wasn't enough for me to embrace abstract classes. The limitation that a class can only inherit from one class was enough to keep me from "wasting" my one and only one inheritance card on an abstract class.

But then I re-read "Framework Design Guidelines" by Cwalina and Abrams and I thought I'd give abstract classes a second chance. They encourage their readers to use abstract classes. I wanted to try out their advice, but I wanted to have a game plan so that I wouldn't completely give up on interfaces. Here are the key points that guide me when choosing between interfaces and abstract classes.

Interfaces Are Weak Contracts

In their capacity as contracts, interfaces are really weak. All an interface can do is enforce method signatures and property types. Interface implementers can make their methods do whatever they want. They can throw whatever exceptions they want to. Interface implementers don't have to respect Barbara Liskov or her Substitution Principle.

Template Methods Provide Contracts With Teeth

A template method is a design pattern where a method calls one or more virtual methods. The template method might have knowledge about how the virtual methods should be called, under what condition they should be called, or what should be done before and after the calls to virtual methods, etc. To some extent the template method design pattern can be used to give a contract some teeth. For example, if a first virtual method does not meet a particular criteria, perhaps the second virtual method won't be called at all. But if the first virtual method does meet the criteria, the second virtual method will be called. What better way to express this template method than as an abstract class?

Core Abstractions Are Likely To Change

If your application's core abstraction is a Map, it is very likely to change over time as you allow your users to do more and more with a Map. It is also likely that different kinds of maps will be introduced which will result in an inheritance hierarchy. When you add the "Print Map" feature, Map may acquire some print functionality which would change its interface. Representing this core abstraction as an interface sets up your users for breaking changes. Representing this core abstraction as an abstract class does not offer perfect isolation from change but it can make change a good deal easier.

Core Abstractions Should Collaborate

I like anthropomorphic objects. If I have a Map and I want to print it, I want to be able to tell the Map to print itself. I don't want to have to instantiate a MapPrinter object and tell it to print the map. However, I don't want my Map object to know about printing APIs. To make Map anthropomorphic without depending on printing APIs, I'd introduce a collaborator that Map can interact with.

C# Supports Single Inheritance Only

As nice as abstract classes are, any given class can at most derive from one of them. This tells me that abstract classes are precious resources. My recently adopted philosophy is that if an abstraction represents essential qualities, it should be modeled as an abstract class. For example in a drawing application, the Shape abstraction represents the essence qualities of squares, circles, and polygons. On the other hand, if an abstraction represents capability or a role, it should be modeled as an interface. For example, shapes might be serializable. Serializability is not part of the shape's essence but it does express one of the Shape's capabilities: the ability to serialize itself.

These five key points guide me as I decide when I should model an abstraction as an interface or as an abstract class. The last point about essential qualities vs. capability is the strongest guide.

The Rub - Unit Testing

Here is the rub with abstract classes that adhere to the template method pattern as I've described it. It's very difficult (impossible?) to mock away abstract classes with non-virtual methods such that no production code for the abstract class winds up in your unit test. Here is where the interface shines; mocking the interface is a snap! So does this mean that I should give up on abstract classes? I don't think so. Abstract classes can be used in unit tests as long as the following criteria are met:

* Abstract classes should have their dependencies injected. If your abstract class connects to a database in its constructor, no amount of mocking will isolate your unit test from the database. The down side of this is that to mock away an abstract class, you may need to create a mock for the abstract class and any dependencies that the abstract class my require during the course of the test.

* Abstract class logic should be tested in isolation from the production classes that derive from it. This can be achieved partly through deriving a number of test-only classes from the abstract class such that the virtual methods of the derived classes put the abstract class logic through its paces and achieve good code coverage. Abstract class logic can also be tested in isolation by injecting different dependencies into the abstract class such that the abstract class' logic is put through its paces.

* Concrete classes derived from the abstract class are also tested in isolation from the abstract class' dependencies. By testing the abstract class and the derived classes independently, you achieve a higher level of certainty that your code will be correct.

20 comments:

王名仁 said...

人有兩眼一舌,是為了觀察倍於說話的緣故。............................................................

于倫 said...

thanks. i like it!>▽<..................................................................

佳皓佳皓 said...

很喜歡看看別人的生活故事,謝謝您的分享哦~~............................................................

吳婷婷 said...

教育的目的,不在應該思考什麼,而是教吾人怎樣思考............................................................

黃威宇 said...

let us be happey everyday!!............................................................

函松函松 said...

知識可以傳授,智慧卻不行。每個人必須成為他自己。. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

佳皓佳皓 said...

要在憂患恥辱的環境裡,創造我們自力更生的新生活。..................................................

黃威宇 said...

知識可以傳授,智慧卻不行。每個人必須成為他自己。. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

淑娟淑娟淑娟 said...

雖然不能常常來看,仍然祝你人氣百分百 ............................................................

劉傑汝劉傑汝 said...

謝謝您的分享~~好文值得收藏!!..................................................................

陳尹v said...

人生有如洶湧的波濤,如果沒有岩石的阻擋,怎能激起美麗的浪花?.......................................................

文王廷 said...

Knowledge is a treasure, but practice is the key to it.................................................

蔡曼鄭美玉屏 said...

Joy often comes after sorrow, like morning after night.. . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

允黃淑 said...

愛,拆開來是心和受兩個字。用心去接受對方的一切,用心去愛對方的所有。......................................................................

彥君李彥君李彥君李 said...

做好事,不需要給人知道,雖然只是一件微不足道的事,但我相信,這會帶給我快樂。..................................................

至馬馬馬馬玄馬馬馬馬 said...

肯定與支持你!!!加油囉~..................................................

玉韓韓韓婷韓韓韓韓 said...

心平氣和~祝你也快樂~~..................................................

瑰潼 said...

很棒的分享~~~來留個言囉~~~~............................................................

張佳穎 said...

君子立恆志,小人恆立志。................. ................................................

王辛江淑萍康 said...

看著你的BLOG 好多朋友都回應 真厲害..................................................