ghytred.com


NewsLook for Outlook

Patterns and OO
RielReview1
The Web Site you seek
Cannot be located, so
Waste your time here

Contents

OO Design Heuristics by Arthur J Riel, Part I

This is made up from extracts from the book above. Its title might be slightly forbidding - I assumed that it was an intermediate to advanced book - but in fact its advice is given in such a simple, clear manner that it is well worth reading at all levels of experience.

To start off, what is a heuristic in this context? The following exchange occurred on news://comp.object:

>Hi all,
>
>some OOD books describe guidelines, other speak about
>heuristics. What is the difference between guidelines
>and heuristics?
>And what differentiates guidelines/heuristics from
>principles?

Principles, heuristics, and guidelines are all aspects of advice.
They aren't rules, laws, or truths. 

Most people impute a hierarchy to the three:

  Principles > Heuristics > Guidelines.

Thus we don't violate principles unless we have to.
We don't violate Heuristics unless we *want* to.
We don't *follow* Guidelines unless we want to.


Robert C. Martin  | "Uncle Bob"                   
Object Mentor Inc.| unclebob @ objectmentor . com

I know I quote Uncle Bob a lot, but he does keep coming up with quotable statements which I agree with.

I have given the heuristics the same numbers as Riel does, so if you get the book (go on! ) you can easily cross-reference from here to there - should you want to. I have also split this up into three sections. This section covers the first third of the book (up to half way through Chapter 3). The second will cover the rest of Chapter 3 and Chapter 4, and the final one the rest of the book with the exception of Chapter 6 on multiple inheritance; I miss this out as we are using C# which doesn't have multiple inheritance and my own experience of it is not enormous (i.e. zero); given that, there's not much I can say usefully.

Classes and Objects: the Building Blocks of the OO Paradigm.

Reference

Heuristic

2.1

All data should be hidden within its class

This fairly standard, but some people appear to dislike it. The main arguments against it I have heard come down to either language limitations (i.e. having to provide getXXX and setXXX methods) or possible resistance to change when moving to a language where the 'cost' of providing these methods is mitigated.

The heuristic states that the following code:

	public class BadCode {
		public string anAttribute;
	}

is bad.

If 'anAttribute' has no logic associated with it - it can freely be set to anything and read in any circumstances - then why not? The answer, the reason behind the heuristic, is that this freedom is not guaranteed to last: you can never say, hand on heart, that this will always be so. If at some later point you need to add some validation to setting the value of 'anAttribute' it's easy to change it to the property form:

	public class NoLongerBad {
		private string anAttribute;
		public string AnAttribute {
			get { return this.anAttribute; }
			set { this.anAttribute = value; }
		}
	}

Sorted. Maybe.

C# compiles this code to two methods, with these signatures:

	public class NoLongerBad {
		private string anAttribute;
		public string getAnAttribute {
			return this.anAttribute; 
		}
		public string setAnAttribute(string value) { 
			this.anAttribute = value; 
		}
	}

All clients of this class which accessed the public member now must be recompiled before they can use this new version. This could be quite an exercise, especially if you have external clients.

Another argument against it is that all accesses are now method calls and they have an overhead. To this all I can say is that - forgetting the possibility of the call being in-lined - if the overhead involved in this is such that it will materially affect your application you must be doing hard real-time against draconian constraints and maybe a high-level language is not appropriate.

The final argument against it is the amount of typing. If you don't use some sort of utility like QuickCode then yes, there is more typing; but if you're compulsively counting key-strokes then remember when you change the code (as you cannot guarantee you won't have to) that you will end up using more key-strokes than you might have done if you did the right thing in the first place! If you do have something like QuickCode then it takes less key-strokes to do the full property version anyway.

When would you want to break this heuristic? Very limited circumstances. I have only come across one type of class where I don't get a bad feeling exposing fields directly: when the class is and always will be a data-only class. For example, when integrating two disparate systems you will be sending 'messages' between these two systems. Your system may well have some or a lot of intelligence about the routing and formats of the messages but the likelihood is that the messages themselves have no intelligence at all, especially in a Service Oriented Architecture where moving objects around is not done. In systems like this the message may well be represented as say an XML Document. Occasionally, I prefer to put this data in a class; it makes accessing individual data items just a bit easier and more efficient. These classes may contain some helper methods, for example to format a Post Code either as a string without spaces or as an in-code and an out-code. In this situation I'm very likely to use data-only classes or structs.

Reference

Heuristic

2.2

Users of a class must be dependent on its public interface, but a class should not be dependant on its users.

Having the user of a class pass itself to a method of the class it is using is ugly and shows high coupling. Sometimes it's sort of required (for instance some of the GoF patterns do this, but the coupling there is intentional); however the ugly coupling can always be got around by making the parameter an Interface which the using class supports (or supplies another class supporting the Interface). In this case the Interface should be defined in the same package (namespace or assembly) as the class receiving it as a parameter to keep coupling down: coupling within an assembly is better than coupling across assemblies.

This promotes reusability - not only across differing systems or clients but within one system or client.

Reference

Heuristic

2.3

Minimise the number of messages in the protocol of a class

By keeping an interface minimal a class is easier to use - and more likely to be used. An interface with many, many messages is an indication that 2.8 is being broken, and is difficult to understand.

 

Reference

Heuristic

2.4

Implement a minimal public interface that all classes support

This is taken care of in .NET as all classes inherit this minimal interface from the Object class. As a reminder, this interface contains:

	bool Equals(object obj);
	int GetHashCode();
	Type GetType();
	string ToString();

Interestingly, he seems to be arguing for a unified type structure, where all types have a common ancestor (or implement a common interface). .NET classes provide this, but C++ classes don't (I don't think IUnknown qualifies, even if it did apply to all). VB6 classes all implement IUnknown and IDespatch, but again these don't qualify. To do this in a language which doesn't natively support it is quite a task, I'd imagine. I haven't felt the need for it before, but given how useful framework classes which take object as a parameter are (ArrayList.Add(), Console.WriteLine() et cetera), that's probably my ignorance and/or inexperience showing.

Reference

Heuristic

2.5

Do not put implementation details such as common-code private functions into the public interface

If it's private, it should be kept so. You don't want to be intimate with everyone.

Reference

Heuristic

2.6

Do not clutter the public interface of a class with items that the users of the class either cannot, do not want to, or should not use.

This is a partner of 2.5. In a sense it tells you whether a method is an implementation detail or not and whether 2.5 applies.

'Should not use' means that if the client calls this method it either will not do what it is intended to - the class's internal state has not been set up properly - and might even corrupt the internal state or throw an exception. 'Cannot use' is a stronger form of this where the method requires prior internal state or parameters whose value the client cannot get hold of or whose type the client is ignorant of. Think Design by Contract here.

Reference

Heuristic

2.7

Classes should only exhibit nil or export coupling with other classes

Four types of coupling:

  • Nil: No coupling at all. This is the nirvana of every designer. Unfortunately a system made up of classes all of which exhibit nil coupling will probably not actually do anything useful.
  • Export: A class is dependant on (uses) the public interface of another class
  • Overt: A class is dependant on (uses) the implementation details of another class (via internal methods, say, or 'friend' in C++).
  • Covert: The same as Overt, but where no permissions have been granted. This can be done via reflection if CAS (Code Access Security) is not implemented correctly.

Not many classes will exhibit Nil coupling, even if we take the liberal view (in this case sensible I think) and exclude all Framework classes. An example would be a class which for instance takes an SQL Connection String in its constructor and SQL Strings in Select, Insert, Update, and Delete methods. Since System.Data.SqlClient is an integral part of the Framework it's reasonable to say that this class has Nil coupling.

However, if the class uses the Microsoft.Data.OdbcClient namespace is the same statement justified? This namespace is not an integral part of the Framework and requires a separate download and installation. I would say that this is definitely Export coupling in that there is an external dependency while the class using System.Data.SqlClient relies on no more than the Framework which will be there anyway (at least if the class has any hope of being called).

Export coupling is the standard usage and we're all OK with it.

Overt coupling is something I have used - via internal methods. Thinking back on it, these usages were sometimes OK and sometimes not: an assembly might have a class with internal scope - using its public methods is fine; marking methods as internal, or using methods marked internal, may not be. The latter certainly increases the coupling and the next time I do it I'll have to ask myself why I'm doing it (the class may have too many responsilities - one to external clients and another to internal ones) and whether the design is really correct.

Covert coupling is an obvious no-no in main-stream systems. I have used it, but only when I'm trying to document or analyse a system. In a main-stream system anyone accessing methods which the design says they shouldn't is on their own.

Reference

Heuristic

2.8

A class should capture one and only one key abstraction.

This is important: A class should capture one and only one key abstraction. Most of the 3.x heuristics repeat and refine this in various ways.

By the way, did I say that this is important? A class should capture one and only one key abstraction.

It's easy to break this. As an example, I have a class which processes Returned items in an Erp integration system. Its main job is to match the item returned to an order and Return Authorisation. It also makes a refund if the matching succeeds. This seemed sensible at the time. However expansion of the Integration system means that Refunds can be given under differing circumstances: I can now get the other circumstances to use this Returns class to make a Refund (silly!), duplicate the refund code across these other classes (also silly) or do what I should have done at the start: put the Refund mechanism in it's own class. It wouldn't have been any more work originally; it's more work now.

Reference

Heuristic

2.9

Keep related data and behaviour in one place

In a Web Shop, the Checkout page should not ask the Basket object for the retail prices of each product, the discount status of the customer, look for any deals which may apply, and then go and calculate the total order price. The behaviour here (calculate the order value) is taking place away from the container of the data required to make the calculation.

Always ask 'Does this calculation really belong here?'. If 'here', wherever that is, has to dig into other objects to get the data to make the calculation the answer is probably 'No, it doesn't belong here'. (This is also related to the Law of Demeter.)

This is a refinement of 2.8.

Reference

Heuristic

2.10

Spin off unrelated information into another class

An extreme example of this is a case where half the methods of a class operate on one half of the data in the class, and the remainder of the methods operate on the other half of the data. You have two classes here. This could happen if, in my Returns class above, I left the Refund code in the Return class and called it directly from the new, non-return, situations.

This is an example of an uncohesive class: it appears to be doing two different things, or doing one thing in two (or more) different ways. In the first case, split it into two classes; in the second, think of the Strategy pattern.

Reference

Heuristic

2.11

Be sure that the abstractions you model are classes and not simply the different roles objects play.

Are Mother and Father classes, or are they roles that a Person class can play? The answer depends on what the system actually is required to do. In a Corporate HR system it is probable that they are roles; in a Hospital system they are probably different classes.

The key is in the behaviour required of them: if at some point in the system it is necessary to send a message to one which cannot ever be sent to the other (e.g. InduceLabour(DateTime.Now)) then they display different behaviour and are different classes.

If there is behaviour which will only ever be called on one of them but could conceivably be called on the other (WashKitchenFloor()) then they may only be different roles. This latter is a grey area. Riel says:

'This point gets convoluted in more abstract domains where it is not clear what cannot be executed versus what a designer or domain chooses not to execute'

He goes on to say that this implies that there is a heuristic he is missing and that this is his best current guess as to what it is, but that he knows it is imperfect.

I have heard people saying things like 'Yes, but you'd never actually call it'. This is a clear indication that you're in this area and some thought needs to be given to the problem.

Topologies of Action-Oriented versus Object-Oriented Applications

I am not 100% sure what Riel means by 'Action-Oriented', but I am fairly confident that 'Procedural' can be substituted without damaging his meaning. He says that Action-Oriented is 'involved with functional decomposition through a very centralised control mechanism', while Object-Oriented 'focuses more on the decomposition of data with its corresponding functionality in a very decentralised setting'.

He goes on to compare best and worst cases of each orientation against the other over an interesting couple of pages, and concludes that Action-Orientation, when done right, looks like Object-Orientation. He then introduces the two main pitfalls of OO: the God class and the Proliferation of classes.

The God Class problem - where too much is centralised into one class - has two forms: behavioural and data. The Proliferation of Classes starts the next in this series.

Heuristics to avoid behavioural God classes

Usually due to an inexperienced designer trying to mimic the central control mechanisms prevalent in Action Oriented programming, the result is a God class which does most of the work delegating minor details to a collection of trivial classes.

Reference

Heuristic

3.1

Distribute system intelligence horizontally as uniformly as possible; the top-level classes in a design should share the work uniformly

Do not design from functional decomposition (e.g. Customer Maintenance, Goods In). Design individual entities (e.g. Customer, Supplier, and Product). The UI of the system is responsible only for getting events in the outside world and passing them on to the appropriate entities in your design and displaying the results.

My personal rule of thumb is that if the domain objects (Customer, Supplier, etc) could not be used pretty much unchanged by a different UI to the one I have in mind I've gone wrong. This different UI should not have to duplicate any logic in the planned UI; I try to imagine a Console Application using my objects (it's been a long time, but they were quite fun!) as well as the GUI I'm actually writing.

Reference

Heuristic

3.2

Do not create God classes. Be very suspicious of classes whose name contains Driver, Manager, System or SubSystem

Just how many responsibilities does this class have? Did I say 2.8 is important?

Note that facades may have these types of names, though.

Reference

Heuristic

3.3

Beware of classes having many accessor (get and set, properties) methods in their public interface. This implies that data and behaviour are not being kept in the same place.

This harks back to 2.9

Again, Facades may look like this.

Reference

Heuristic

3.4

Beware of classes that have too much non-communicating behaviour.

I'm not exactly sure what he means here. My guess, given the context of God classes, is that a class which does a lot of work itself without delegating parts of that work to other classes is to be avoided. With this I agree. Classes that do this will tend to be over-complex and difficult to understand and hence maintain. To get away from a class that does this look to the Mediator pattern and refactor the work into already existing classes or new classes (Strategy, Command, Visitor) if no suitable abstractions already exist.

By the way, if anyone wants some practice on this I have a wonderful pair of classes called OrderStatusProcessor and ReturnsProcessor which you can practice on.

Facades can easily break all four of these heuristics. It is probable that a Facade will always break 3.2 (naming), usually break 3.3 (lots of accessors) and almost by definition breaks 3.1 (distribution of intelligence); however as long as it doesn't break 3.4 - in other words, as long at most of its work is delegated - then it's probably OK. This just means that Facades aren't as easy to do properly as they look at first. I haven't done many and I'm not really happy with any of the ones I have done: not enough Mediator/Strategy patterns used probably.

Reference

Heuristic

3.5

In applications which consist of an OO model interacting with a UI, the model should never be dependant on the UI.

My comments on 3.1 apply.

Reference

Heuristic

3.6

Model the real world whenever possible.

Riel immediately notes 'This heuristic is often violated for reasons of system intelligence distribution [3.1], avoidance of God classes [3.x] and keeping related data and behaviour in one place [2.9]'

This is a heuristic I have real doubts about. I agree that it's an excellent place - the only place, even - to start your analysis and design but very soon your model will depart from the real world.

In the interests of fairness, I must say that there are successful and respected designers who swear that they never depart from the real world. Still in the interests of fairness (to myself) I must also say that I don't believe them for a minute: when did you last see an abstract product? Or an abstract anything? Or a Hashtable walking by? We possibly mean different things by 'reality'....

Heuristics to avoid Data God classes

This form of the problem - classes build around a data Structure - occurs very often when migrating a legacy system to a new OO design.

Riel gives no heuristics devoted to the problem of data God Classes. He discusses Controller and Entity classes as a popular but incorrect way to do legacy migration.

The algorithms in these legacy systems are expressed in terms of separate data structures and code blocks. These data structures are often used by many different code blocks and composing these into discrete Objects encapsulating data and associated behaviour (or behaviour and associated data) can be a very complex task. It's far easier to bung the data into a data-holding class and the behaviour into (what might as well be static) methods, thus more or less exactly duplicating the structure of the legacy application. This is one of the reasons for the popularity of the Controller/Boundary/Entity type of analysis, which has spread out from legacy migrations to be used in completely new systems.

I really dislike this split.

An excellent post from Paul Campbell on comp.object says this about them:

"kk_oop @yahoo.com>" <"kk_oop wrote in message news:bnln92$oah$1@bob.news.rcn.net...
> Hi.
>
> I'm looking for opinions on using robustness class stereotpes, namely,
> Boundary, Control and Entity classes.  My sense is that these are useful
> categorizations for moving from use case narrative to actual design.

I'm going to be alot less charitable about them that HS ...

IMO they are all but useless for anything.  They are a rehash of
70's "data processing systems analyis" contructs of process (control),
datastore (entity), and terminal (boundary). They hark from a time
when "analysis" consisted of a horrid hybrid of high level database
structure design and the deployment structure of functional sub-systems
that accessed them.

They are IMO absolutely useless for describing modern n-tiered
distributed systems whose architecture is often orthogonal to the
functional requirements.

> This is especially good for folks just coming onto the OO train, as it
> forces them to consider encapsulation based on these classes criteria.
> It also seems like it would aid design reviews by showing visually that,
> for instance, interfaces (Boundary) and passive/persistant objects
> (entity) have been isolated from busniess rules (Control).

Yes but the whole idea is that you arnt supposed to seperate the two -
your "business logic" IS the behaviour of your core classes.

These stereo types encorage a muddling of a requirements analysis
and architecture, and if used at the design level the positively dangerous
practice of designing a data model with objects rather than a true
object model.

Paul C.

Mipsleddings and all, it perfectly articulates my view of the Boundary-Controller-Entity split.

Riel says about the problem of Legacy migration 'There is an enormous amount of interest in finding a good process for this migration...[but this is] outside the scope of this text' and then concludes that yes, it's a hard problem.

To return to data God classes, the point is that creating them imitates the data/behaviour split from previous styles of programming. The essence of OO is that objects combine data and behaviour; ignoring this looses all the advantages OO can give.

It might be interesting to consider .NET DataSets, both strongly-typed and vanilla, in this light.

Return to top