Managing Change through OOAD by Ken Sipe

By: Ken Sipe

Abstract: Understanding the behavior of objects and answering tough questions like what is the likelihood of change? are critical in designing applications that will last longer than the first vision.

Managing Change through OOAD

By Ken Sipe

kensipe@codementor.net

 

All systems change during their life cycles. This must be

borne in mind when developing systems expected to last longer than the first version.

-- Ivar Jacobson

Introduction

When we think of change management in software development terms, we usually tend to think of requirements management with its requirements trace matrix or code management through version control systems. Visions of constraining a stake holder, to the first thought they mistakenly put to paper, then making them pay through endless levels of bureaucracy for any change they may have come to mind. Then of course after realizing the requirements keep changing, the lesson on version code management would be in dire need. So we branch any new idea then within the last couple of days before release of the software, we merge 10 of 12 branches and releasing. Sounds simple enough! Well while the project managers and the business analysts learn new techniques on how to drive us crazy, lets examine ways to develop object-oriented software which is resilient to change.

 

Ive often consider software development or the act of designing and writing software to be closer to philosophy than science of course it doesnt help that I just finished reading Zen and the art of motorcycle maintenance. The fact is there should be a constant voice in your head as youre developing asking the question what is the likelihood of change here? , the other voice should be asking you about the level abstraction you are looking at and should you look at the problem another way, using Phaedrus knife so to speak. It is more about how you analyze code which makes you good at coding It is more about understanding the details of a certain level of abstraction. It is more about understanding certain principles of development and applying them. Good object-oriented code is less about structure and more a matter of behavior. Learning object-oriented concepts such as inheritance, or encapsulation is just the start. Lets look at several principles of correct application to improve the quality of our software solutions, and what effects these principles have on change management.


Implementing your Analysis

One significant problem I run into is what I refer to as Implementing your Analysis. By analysis, I specifically refer to the object oriented diagrams which are suppose to be language agnostic. Often, teams do not make this distinction, mainly because they are a Java shop (or pick your language). As well, working with analysis models is working with abstractions which is another skill level entirely. Many times analysis is avoided with the justification that it doesnt produce an executable product. The language and frameworks are known, so analysis is skipped and we move right into design. However in many cases analysis is folded right into the design work. The distinction between analysis and design often gets cloudy; however it is really a matter of abstractions. Lets see if we can detect the difference.

 

A common practice for jump starting OO analysis is to look at the use case or stories for nouns. These nouns will be our first set of objects, often referred to as Business Objects, because they reflect or often trace back directly to real business entities. As the set of business objects grows, we start to try to understand their relationship with each other. While this is a good exercise in analysis to better understand the relationships of the business, too often the analysis is what is implemented. The situation is made easier and more comfortable by the fact that the same type of activity is conducted by the DBA coming up with initial crack at the schema. However, OO is about behavior not structure.

Example 1: Its about Behavior

Lets look at a real-world example. At the time I was working for a genetics research company, there were millions of alleles (just think of them as strands of DNA) which were produced as an output of one process. That information needed to be exported from one data store to another. One of the OO designers of the system was having problems with the early incarnations of the system because the system was getting bogged down from a resource and time constraint based on the instantiating millions of allele objects so that the object could persist itself in the other data store. This may seem like a simple example but it is at the heart of good OOAD and at the core of being resilient to change. The allele objects looked great on paper, they made sense from an academic stand point, but that is not the behavior of the system. The behavior of the system is to export and import data. It is to move data from one source to another. Yes, of course, there must be some validation involved Sure something must validate that the moving data is consistent, complete and that validation might be done in an Allele Object or an AlleleValidator. By focusing on behavior, the system becomes extensible. Given a source and a destination with object maps, the system could be adapted to import / export proteins or genetic markers. Stealing a quote from the Matrix, Its the question that drives us and the question is: What is the likelihood of change? Where is it? Is it the data mapping? Is it the type of data which is exported? The answer is out there Its looking for you.

 

Example 2: Inheritance at the right abstraction

Now for you picture people out there a more hypothetical but illustrative example. Lets concern the development of a University registration system. Reading through the use cases or stories we see the need for a student object, however there are different types; full-time students and part-time students. So, we logically see an abstraction of student, which would be a super class for the two student types. Looking further, the student will have courses so we might have a model which looks like the following:

 

 

Figure 1 Student Class Diagram Analysis

While often this is referred to as design It is much closer aligned to analysis. Lets dig deeper:

  1. What happens if a part-time student becomes a full time student? How does an object morph from one object type to another?

This is the most significant issue, the morphing of one type of object to another type. This stems from the structural inheritance which will be detailed later in this article. An implementation of this model at run time would leave you with 2 choices for the transition of a part-time student to a full time student: the existence of 2 objects at one time which represent 1 entity from the real world, or the zero objects at one point in time. Either approach results in inconsistencies in the system at one point in time.

  1. How do we handle the addition of another classification of student?

This is a fairly simple example and the likelihood of another classification is unlikely for most systems. However if you extend this out to other similar situations, or if you can imagine another classification of a student there are significant issues. The most significant are identified with morphing; after creating another subclass, all the morphing code will have to change. The situation would also exacerbate the next identified issue.

  1. Can a PartTimeStudent be used any where in the system a Student can be used? Or will you end up with code which checks for the instance of a known type?

If there are areas of code which check for instance types to perform specific functionality, then the design is not extensible. In short, it doesnt follow the open-close principle which will be detailed later. Thus, small changes will ripple through large sections of code.

A More Refined Solution

It must be restated that this relationship makes sense from an analysis perspective, but dont let it creep into your design. So how did we get here? A part-time student is a student. It passes the is a relationship test. It should be good, right? Lets look at how we might change the design to address the key issues.

 

Figure 2 Student Class Diagram Design

 

In this case the student has a student classification, which in turn has specialized extensions. The beauty here is the relationships make sense, and there is no morphing issue with the student. In this case a student classification can change without losing the identity of the student or its relation with its courses.


Principles of Inheritance

It has been my experience that improper use of inheritance is the largest contributor to poor object-oriented design. Understanding certain key principles pertaining to inheritance must be understood in order to develop good object-oriented systems. Lets start with the reasons to inherit:

  1. Structural  Object 2 has structure which is similar to Object 1.
  2. Functional  Object 2 has functions which are desired by Object 1.
  3. Behavior  Object 2 is a specialized version of Object 1.

From a quality of use or best practices stand point these are listed in the worst reason to best reason to use inheritance. The most important factor is to know why you are inheriting. By increasing the quality of the type of inheritance the quality of the design is increased which in many cases is more resilient to change. Lets discuss each type in further detail.

Structural Inheritance

Structural Inheritance is the least desirable reason to inherit. The main reason should be obvious. Basically, it provides little to no benefit outside of code reuse. There is little or no benefit of polymorphic behavior or encapsulation. In systems with a high degree of structural inheritance, as a class is extended for its structure, then all the relating classes seem to need to be extended to accommodate the new structure. This does not agree with the substitution principle or the open-close principle outlined below. A change of structure causes a ripple effect across a number of the classes in the code base.

Functional Inheritance

The difference between functional and behavior is simply the need for a method contained in an object vs. the extension of a specific type of object. It seems best illustrated with an example. In this example, we are building a system where we will need a file path. In analyzing the model we have to decide between 2 approaches illustrated below.

 

Figure 3 Inheritance vs. Aggregation Model

Remember Its the question that drives us. So what are the questions?

Is FilePath a String?

It will be a String which represents a filepath. It seems to make sense.

Does FilePath contain a String?

I guess it could.

The more important question is what does a String do? And what does a FilePath do?

First why is this question more important? Basically because it defines behavior and object-orient development is all about behavior. So what is a String? It is an array of chars, which can be trimmed, we can get index of chars and strings, we can sub-string.

So what is a FilePath? It is a string which represents the location of a file. What does it do? It can validate if a file exists, it can give you the drive, the path, the file extension.

 

It would seem that you would want to use methods of a string to get provide the functionality of the FilePath. You need to do a sub-string to provide most of the functionality we want the FilePath to have. However, if FilePath inherits from a String then any client of FilePath would be able to sub-string any part of the FilePath, this would break encapsulation. The very thing FilePath was responsible for is corruptible by inheriting from a String. Now some of you C++ heads out there are saying well I could inherit privately, thus protecting the integrity of the FilePath object. While this is true, later principles will illustrate that this is still poor object design. Thus aggregation is a better approach for this design and hopefully demonstrates what is meant by functional inheritance.

Behavior Inheritance

Behavior inheritance is inheritance which is truly an extension of another objects behavior. In the ideal situation an instance of a subclass can be passed to a method which expects the super class, this known as Liskov Substitution Principle. Could you image in our previous example passing an instance of a FilePath to any method which has a String as a parameter? It seems intuitive at this point that it doesnt make sense.

Liskov Substitution Principle

Barbara Liskov in conjunction with Jeannette Wing presented in 1993 a paper entitled Family Values: A Behavioral Notion of Subtyping containing the liskov substitution principle. The principle states If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T. [Liskov88]. WOW! Read that a few times fast Basically restated; an instance of an extension of a class, should be able to be passed to a method expecting the base class with no consequence or side effect. This principle is at the heart of good inheritance. By following this principle designs begin to be extensible, which brings us to the next principle.

Open / Close Principle

Coined by Bertrand Meyer, and expounded upon by Robert C. Martin, the open-close principle basically means that units of code, namely classes and methods, are open for extension and closed for modification. This principle works congruently with the substitution principle. Applying this principle, code is closed to modification If change is required the only solution is to extend. Of course it takes a significant amount of design effort to reach this level of maturity; the benefits being a significant resilience to change.


Conclusion

Most books on the subject would have us believe OOAD is about polymorphism, inheritance and encapsulation. While these are cool buzzwords and powerful tools in the object-oriented arsenal there is more to a good object-oriented design. OOAD is about behavior. Object-Oriented systems developed around behavior are more resilient to change.

 

Understanding the behavior of objects and answering tough questions like what is the likelihood of change? are critical in designing applications that will last longer than the first vision.

 

Acknowledgements

There are a number of leaders which have helped to form the views expressed in this article which we should pay homage to, in particular the work of Barbara Liskov, Jeannete Wing and Robert C. Martin. They have had a huge impact on shaping the discipline of our industry.

 

I would also like to personally thank Ron Campbell, the great wordsmith and musician, who made this document readable.

 

Further Reading

http://www.objectmentor.com/resources/articles/lsp.pdf

http://javaboutique.internet.com/tutorials/JavaOO/

http://www.pmg.csail.mit.edu/~liskov/
http://www.cs.cmu.edu/~wing/

http://www.eventhelix.com/RealtimeMantra/Object_Oriented/open_closed_principle.htm

 


Ken Sipe is founder and CTO of Code Mentor, Inc. He has been an instructor and mentor for OOAD, RUP, UML and Agile methodologies. He further mentors clients in many Java technologies such as J2EE, Struts, Spring and Hibernate and is often called on to rescue projects. Ken has been a speaker at JavaOne, NFJS and at every Borland Conference since 2000.


Server Response from: ETNASC03