The Dependency Inversion Principle


The Open-Closed Principle

Software systems change over time. Change takes many forms, but changing and evolving system requirements provide the primary catalyst. A software system must accommodate change. It must evolve gracefully throughout its useful life cycle. A software system that is rigid, fragile, and change-resistant exhibits bad design. A software system that is resilient, flexible, and extensible possesses the hallmark characteristics of a well-founded object-oriented architecture. The open-closed principle (OCP) provides the necessary framework for achieving an extensible and accommodating software architecture.

Formulated by Bertrand Meyer, the open-closed principle makes the following assertion:

Software modules must be designed and implemented in a manner that opens them for extension but closes them for modification.

Said another way, changes to software modules should be avoided and new system functionality added by writing new code. It should be noted that writing code that is easy to extend and maintain is a requirement in and of itself. Writing such code takes longer initially but pays a big dividend later. I call it the design dividend.

Achieving The Open-Closed Principle

The key to writing code that conforms to the open-closed principle is to depend upon abstractions, not upon implementations. The reason — abstractions tend to be more stable. (Correctly designed abstractions are very stable!) This is achieved in Java through the use of abstract base classes or interfaces and dynamic polymorphic behavior. Code should rely only upon the interface methods and behavior promised via abstract methods. A code module that relies only upon abstractions will exhibit the characteristic of being closed to the need for modification yet open to the possibility of extension.

An OCP Example

A good example of code written with the OCP in mind given in examples 24.13 through 24.21. This code implements a simple naval fleet model where vessels of various types can be constructed with different types of power plants and weapons. Figure 24-7 gives the UML diagram for the naval fleet class inheritance hierarchy. Example 24.22 offers a short program showing the naval fleet classes in action, and figure 24-8 shows the results of running this program.

Example 24.13: Vessel.java

image from book
 1     public abstract class Vessel { 2       private Plant its_plant = null; 3       private Weapon its_weapon = null; 4       private String its_name = null; 5 6       public Vessel(Plant plant, Weapon weapon, String name){ 7         its_weapon = weapon; 8         its_plant = plant; 9         its_name = name; 10        System.out.println("The vessel " + its_name + " created!"); 11      } 12 13      /* ******************************************************** 14          Public Abstract Methods - must be implemented in 15          derived classes. 16      *********************************************************/ 17      public abstract void lightoffPlant(); 18      public abstract void shutdownPlant(); 19      public abstract void trainWeapon(); 20      public abstract void fireWeapon(); 21 22      /* ******************************************************** 23          toString() Method - may be overridden in subclasses. 24      *********************************************************/ 25      public String toString(){ 26        return "Vessel name: " + its_name + " " + its_plant.toString() + 27                " " + its_weapon.toString(); 28      } 29      protected Weapon getWeapon(){ return its_weapon; } 30      protected Plant getPlant(){ return its_plant; } 31 32    }// end Vessel class definition
image from book

Example 24.14: Plant.java

image from book
 1     public abstract class Plant { 2        private String its_model = null; 3        public Plant(String model){ 4          its_model = model; 5        } 6        public abstract void lightoffPlant(); 7        public abstract void shutdownPlant(); 8 9        public String toString(){ return "Plant model: " + its_model; } 10    }
image from book

Example 24.15: Weapon.java

image from book
 1     public abstract class Weapon { 2       private String its_model = null; 3 4       public Weapon(String model){ 5         its_model = model; 6         System.out.println("Weapon object created!"); 7       } 8 9       public abstract void trainWeapon(); 10      public abstract void fireWeapon(); 11 12      public String toString(){ return "Weapon model: " + its_model; } 13    }
image from book

Example 24.16: CIWS.java

image from book
 1     public class CIWS extends Weapon { 2 3        public CIWS(String model){ 4         super(model); 5         System.out.println("CIWS object created!"); 6        } 7 8        public void trainWeapon(){ 9         System.out.println("CIWS is locked on target!"); 10       } 11 12       public void fireWeapon(){ 13        System.out.println("The CIWS roars to life and fires a zillion bullets at the target!"); 14       } 15    }
image from book

Example 24.17: Torpedo.java

image from book
 1     public class Torpedo extends Weapon { 2 3       public Torpedo(String model){ 4        super(model); 5        System.out.println("Torpedo object created!"); 6       } 7 8       public void trainWeapon(){ 9        System.out.println("Torpedo is locked on target!"); 10      } 11 12      public void fireWeapon(){ 13       System.out.println("Fish in the water, heading towards target!"); 14      } 15    } 
image from book

Example 24.18: Five_Inch_Gun.java

image from book
 1     public class Five_Inch_Gun extends Weapon { 2 3       public Five_Inch_Gun(String model){ 4        super(model); 5        System.out.println("Five Inch Gun object created!"); 6       } 7 8       public void trainWeapon(){ 9        System.out.println("Five Inch Gun is locked on target!"); 10      } 11 12      public void fireWeapon(){ 13       System.out.println("Blam! Blam! Blam!"); 14      } 15    }
image from book

Example 24.19: SteamPlant.java

image from book
 1     public class SteamPlant extends Plant { 2 3        public SteamPlant(String model){ 4          super(model); 5          System.out.println("SteamPlant object created!"); 6        } 7 8        public void lightoffPlant(){ 9          System.out.println("Steam pressure is rising!"); 10       } 11 12       public void shutdownPlant(){ 13         System.out.println("Steam plant is secure!"); 14       } 15    }
image from book

Example 24.20: NukePlant.java

image from book
 1     public class NukePlant extends Plant { 2 3        public NukePlant(String model){ 4          super(model); 5          System.out.println("NukePlant object created!"); 6        } 7 8        public void lightoffPlant(){ 9          System.out.println("Nuke plant is critical!"); 10       } 11 12       public void shutdownPlant(){ 13         System.out.println("Nuke plant is secure!"); 14       } 15    }
image from book

Example 24.21: GasTurbinePlant.java

image from book
 1     public class GasTurbinePlant extends Plant { 2 3        public GasTurbinePlant(String model){ 4          super(model); 5          System.out.println("GasTurbinePlant object created!"); 6        } 7 8       public void lightoffPlant(){ 9         System.out.println("Gas Turbine is running and ready to go!"); 10      } 11 12      public void shutdownPlant(){ 13        System.out.println("Gas Turbine is secure!"); 14      } 15    }
image from book

image from book
Figure 24-7: Naval Fleet Class Inheritance Hierarchy

Example 24.22: FleetTestApp.java

image from book
 1     public class FleetTestApp { 2       public static void main(String[] args){ 3         Vessel v1 = new Submarine(new NukePlant("Preasureized Water Mk 85"), new Torpedo("MK 50"), 4                               "USS Falls Church"); 5 6         v1.lightoffPlant(); 7         v1.trainWeapon(); 8         v1.fireWeapon(); 9         v1.shutdownPlant(); 10 11      } 12    }// end FleetTestApp class definition
image from book

image from book
Figure 24-8: Results of Running Example 24.22

Quick Review

The open-closed principle (OCP) attempts to optimize object-oriented software architecture design so it can accommodate change. Software modules should be designed so they are closed to modification yet open to extension. The OCP is achieved by depending upon software abstractions. In Java this means designing with abstract base classes or interfaces while keeping the goal of dynamic polymorphic behavior in mind. The OCP relies heavily upon the Liskov substitution principle and design by contract (LSP/DbC).




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net