Design Principles: Part 2

Similar documents
Outline. Design Principles: Part 2. e.g. Rectangles and Squares. The Liskov Substitution Principle (LSP) ENGI 5895: Software Design.

Design Principles: Part 2

Conception Orientée Objets. Programmation SOLID

Software Engineering CSC40232: SOFTWARE ENGINEERING. Guest Lecturer: Jin Guo SOLID Principles sarec.nd.edu/courses/se2017

Liskov Substitution Principle

OO Class Design Principles

Test Code Patterns. How to design your test code

Last Time: Object Design. Comp435 Object-Oriented Design. Last Time: Responsibilities. Last Time: Creator. Last Time: The 9 GRASP Patterns

Software Engineering I (02161)

Testing and Inheritance. Test Code Patterns. Inheritance-related bugs. Inheritance. Testing of Inheritance. Inheritance-related bugs

Object Oriented Software Design - I

Software Engineering

17.11 Bean Rules persistent

Principles of Object-Oriented Design

S.O.L.I.D: Software Engineering Principles

Object-oriented design principles

Chapter 6: Inheritance

CSC207 Week 3. Larry Zhang

Principles of OO Design

Introduction to Object-Oriented Programming

Component-Level Design

Programming in the large

Lecture: Modular Design

a correct statement? You need to know what the statement is supposed to do.

OOD Smells and Principles

SOLID Principles. Equuleus Technologies. Optional Subheading October 19, 2016

Bruno Bossola SOLID Design Principles

Outline. Subtype Polymorphism, Subtyping vs. Subclassing, Liskov Substitution Principle. Benefits of Subtype Polymorphism. Subtype Polymorphism

CMPS 115 Winter 04. Class #10 (2004/02/05) Changes/Review Programming Paradigms Principles of OOD <break> Design Patterns

Assertions. Assertions - Example

OO Design Principles

SOLID: Principles of OOD

n HW5 out, due Tuesday October 30 th n Part 1: Questions on material we ll cover today n Part 2: BFS using your graph from HW4

Outline. Inheritance. Abstract Classes Interfaces. Class Extension Overriding Methods Inheritance and Constructors Polymorphism.

S.O.L.I.D. Principles of

COURSE 2 DESIGN PATTERNS

Object-Oriented Design

Intro to: Design Principles

Readability [Skrien 4.0] Programs must be written for people to read, and only incidentally for machines to execute.

Single Responsibility Principle

11/2/09. Code Critique. What goal are we designing to? What is the typical fix for code smells? Refactoring Liskov Substitution Principle

Object-Oriented Design

6.170 Recitation #5: Subtypes and Inheritance

Software Engineering Design & Construction Dr. Michael Eichberg Fachgebiet Softwaretechnik Technische Universität Darmstadt

Object Oriented Programming and Design in Java. Session 10 Instructor: Bert Huang

The Liskov Substitution Principle

Open Closed Principle (OCP)

Class Design Principles

11/4/15. Review. Objec&ves. Refactoring for Readability. Review. Liskov Subs&tu&on Principle (LSP) LISKOV SUBSTITUTION PRINCIPLE

CHAPTER 5: PRINCIPLES OF DETAILED DESIGN

Index. Index. More information. block statements 66 y 107 Boolean 107 break 55, 68 built-in types 107

Software Engineering Design & Construction Dr. Michael Eichberg Fachgebiet Softwaretechnik Technische Universität Darmstadt

References: internet notes; Bertrand Meyer, Object-Oriented Software Construction; 10/14/2004 1

CS 520 Theory and Practice of Software Engineering Fall 2017

CS 320 Introduction to Software Engineering Spring March 06, 2017

Single Responsibility Principle (SRP)

Software Engineering Design & Construction

Highlights of Previous Lecture

CS 520 Theory and Practice of Software Engineering Fall 2018

1 Introduction 2 Complex Systems 3 Object Model 4 Dependency Management 5 Class Design. Object Oriented Programming in Physics

Coverage of Part 2. A Brief Introduction to Java for C++ Programmers: Part 2. import: using packages

CSC Advanced Object Oriented Programming, Spring Specification

Principles of Ruby Applica3on Design. Dean Wampler Senior Mentor and Consultant Object Mentor, Inc. Chicago, IL

Software Design and SOLID Principles

JAVA PROGRAMMING LAB. ABSTRACT In this Lab you will learn how to describe objects and classes and how to define classes and create objects

Originality is Overrated: OO Design Principles

Object-Oriented Design I - SOLID

Learning Objectives. C++ For Artists 2003 Rick Miller All Rights Reserved xli

C++ Modern and Lucid C++ for Professional Programmers

CSC207H: Software Design SOLID. CSC207 Winter 2018

Chapter 10 Classes Continued. Fundamentals of Java

Abstract and final classes [Horstmann, pp ] An abstract class is kind of a cross between a class and an interface.

Outline. Software Rots

Inheritance and object compatibility

Design Patterns: Part 2

Defining Classes and Methods. Objectives. Objectives 6/27/2014. Chapter 5

[ L5P1] Object-Oriented Programming: Advanced Concepts

Software Engineering /48

Plan. Design principles: laughing in the face of change. What kind of change? What are we trying to achieve?

Advanced JML Erik Poll Radboud University Nijmegen

CPSC 410? Advanced Software Engineering Mid-term Examination (Term I ) SOLUTION Instructor: Gail Murphy

Chapter 11 Classes Continued

Subtypes and Subclasses

Defining Classes and Methods

CMPSCI 187: Programming With Data Structures. Lecture 6: The StringLog ADT David Mix Barrington 17 September 2012

CISC 3115 TY3. C09a: Inheritance. Hui Chen Department of Computer & Information Science CUNY Brooklyn College. 9/20/2018 CUNY Brooklyn College

Designing Robust Classes

Introduction to Object-Oriented Programming

Build Testable Client and Service Applications

CSCI-142 Exam 1 Review September 25, 2016 Presented by the RIT Computer Science Community

Lessons Learned. Johnny Bigert, Ph.D., Skype/Microsoft October 26, 2011

Object-Oriented Design II

The following topics will be covered in this course (not necessarily in this order).

Abstraction. Design fundamentals in OO Systems. Fundamental Software Development Principles

CS 315 Software Design Homework 3 Preconditions, Postconditions, Invariants Due: Sept. 29, 11:30 PM

Abstract Class. Lecture 21. Based on Slides of Dr. Norazah Yusof

17. Assertions. Outline. Built-in tests. Built-in tests 3/29/11. Jelle Slowack, Bart Smets, Glenn Van Loon, Tom Verheyen

Summary of the course lectures

17. Assertions. Jelle Slowack, Bart Smets, Glenn Van Loon, Tom Verheyen

Inheritance and Polymorphism in Java

Transcription:

Liskov Substitution Principle (LSP) Dependency-Inversion Principle (DIP) Interface-Segregation Principle (ISP) Design Principles: Part 2 ENGI 5895: Software Design Andrew Vardy Faculty of Engineering & Applied Science Memorial University of Newfoundland January 30, 2017

Liskov Substitution Principle (LSP) Dependency-Inversion Principle (DIP) Interface-Segregation Principle (ISP) Outline 1 Liskov Substitution Principle (LSP) 2 Dependency-Inversion Principle (DIP) 3 Interface-Segregation Principle (ISP)

Liskov Substitution Principle (LSP) Dependency-Inversion Principle (DIP) Interface-Segregation Principle (ISP) The Liskov Substitution Principle (LSP) Subtypes must be substitutable for their base types. You might think that this is enforced by your language (i.e. Java, C++,...) e.g. If Gorilla inherits from Animal then I should be able to substitute a Gorilla whenever I m asked to supply an Animal However, violations of the LSP are often quite subtle The LSP is violated whenever the reasonable expectations of a user of a base class are not met for the derived class

e.g. Rectangles and Squares Consider the following class hierarchy: This seems quite natural. Isn t a square just a particular type of rectangle? It is hard to foresee any problems with this logic. Yet problems arise...

Consider our Rectangle class: class Rectangle { protected Point topleft ; protected double width, height ; public void setwidth( double w ) { width = w ; public void setheight( double h ) { height = h ; public double getwidth () { return width ; public double getheight () { return height ; public double getarea () { return width height ; The first hint of trouble comes when we consider how to maintain both width and height. Our first attempt to address this problem will utilize only the width attribute...

class Square1 extends Rectangle { @Override public void setwidth( double w ) { width = w ; @Override public void setheight( double h ) { width = h ; public class Tester1 { public static void testheight( Rectangle r ) { r. setheight (10); assert r. getheight () == 10; public static void main( String [] args) { testheight( new Rectangle ()); testheight( new Square1 ()); The second test fails! Square1 is not substitutable for Rectangle w.r.t. testheight. Lets try again, with Square2 always keeping width and height the same...

class Square2 extends Rectangle { @Override public void setwidth( double w ) { width = w ; height = w ; // Make the Square stay square @Override public void setheight( double h ) { height = h ; width = h ; // As in setwidth public class Tester2 { public static void testarea( Rectangle r ) { r. setwidth (5); r. setheight (4); assert r. getarea () == 20; public static void main( String [] args) { testarea( new Rectangle ()); testarea( new Square2 ()); This fails again!! Square2 is not substitutable for Rectangle w.r.t. testarea. Thesubstitutionprincipleisviolated!

Design by Contract This situation might have been avoided by adding sufficiently strong pre- and post- conditions. Consider the following postcondition for Rectangle s setwidth(double w) method: postcondition: width 0 == w and height 0 == height If tested (via assert) this postcondition would have detected the error in Square2 (for Square1 a corresponding postcondition for setheight would have been required). Note that preconditions and postconditions for derived classes should follow Meyer s rule: A routine redeclaration [i.e. an overridden method] may only replace the original precondition by one equal or weaker, and the original postcondition by one equal or stronger.

Liskov Substitution Principle (LSP) Dependency-Inversion Principle (DIP) Interface-Segregation Principle (ISP) e.g. Lines and LineSegments We want to represent both lines and line segments. Considering how much these classes have in common, we adopt Line as the base class: public class Line { protected Point p1, p2 ; public Line( Point p1, Point p2) {/ / public double getslope () {/ / public double getintercept () {/ / public boolean ison( Point p ) {/ /

public class LineSegment extends Line { public LineSegment( Point p1, Point p2) {/ / public double getlength () {/ / @Override public boolean ison( Point p ) {/ / Reasonable assumption for a user of Line: e.g. For all points p lying on the infinite line, ison(p) returns true p = new Point(0, line.intercept) We expect that line.ison(p) returns true However, in some cases this will not be the case for LineSegment! This is a violation of the substitution principle!

Solution Factor out the common elements of Line and LineSegment into a new superclass that both inherit from. The common elements here includes everything but ison. This new superclass, LinearObject, is made abstract since we do not want to instantiate it. public abstract class LinearObject { protected Point p1, p2 ; public LinearObject( Point p1, Point p2) {/ / public double getslope () {/ / public double getintercept () {/ / public abstract boolean ison( Point p );

Concrete classes Line and LineSegment extend the abstract class LinearObject: public class Line extends LinearObject { public Line( Point p1, Point p2) {/ / @Override public boolean ison( Point p ) {/ / public class LineSegment extends LinearObject { public LineSegment( Point p1, Point p2) {/ / @Override public boolean ison( Point p ) {/ / It would be easy to add further linear objects such as Ray.

Liskov Substitution Principle (LSP) Dependency-Inversion Principle (DIP) Interface-Segregation Principle (ISP) Relation to OCP The LSP enables the OCP: e.g. LinearObject is now open for extension by inheriting new derived classes from it. Behavior is added by adding code as opposed to changing existing code

Liskov Substitution Principle (LSP) Dependency-Inversion Principle (DIP) Interface-Segregation Principle (ISP) The Dependency-Inversion Principle (DIP) 1 High-level modules should not depend on low-level modules. Both should depend on abstractions. 2 Abstractions should not depend on details. Details should depend on abstractions. Another way of stating this principle is this: Depend on abstractions. What this means in practise is that we should break out the essentials of every dependency into an interface or abstract class.

e.g. Consider a high-level Policy Layer module and the two lower-level modules it depends on: In applying the DIP we replace direct dependencies with interfaces:

e.g. Consider a simple Button object that is able to determine if a button has been pressed. We wish to connect this Button to an object such as a Lamp to turn it on or off. We assume that some higher-level code is calling the Button s poll method repeatedly. This yields the following implementation for Button: public class Button { private Lamp lamp ; public void Poll () { if ( / Button was pressed / ) lamp. TurnOn (); What s wrong with this? What if we want to use Button to control a Motor or Car class? Button.java must change.

The job of a button is to detect button presses, not talk to lamps! The current design can be viewed as violating the single-responsibility and open-closed principles as well. The solution is to invert the dependency of Buttons on Lamps and for Button to instead depend on a new interface:

e.g. Furnace Consider a simple furnace control system (written in C++). The software reads temperature from an IO channel and tells the furnace to engage or disengage through another channel. # define THERMOMETER 0 x86 # define FURNACE 0 x87 # define ENGAGE 1 # define DISENGAGE 0 void Regulate( double mintemp, double maxtemp) { for (;;) { while ( in( THERMOMETER) > mintemp) wait (1); // Wait 1 sec. out( FURNACE, ENGAGE ); while( in( THERMOMETER) < maxtemp) wait (1); out( FURNACE, DISENGAGE ); The high-level algorithm (wait to cool, then turn on furnace, wait to heat up, turn off furnace) is mixed up with the low-level details of checking the temperature and turning on/off the furnace.

The following UML diagram shows the application of the DIP (note how stereotypes are used to alter the default meaning of symbols). Regulate now depends on interfaces, not IO channels.

Here is the code: void Regulate( Thermometer &t, Heater &h, double mintemp, double maxtemp) { for (;;) { while ( t. Read () > mintemp) wait (1); h. Engage (); while ( t. Read < maxtemp) wait (1); h. Disengage (); Notice that the algorithm itself is more cleanly expressed. Most importantly, we are insulated from changes to the thermometer and heater.

Liskov Substitution Principle (LSP) Dependency-Inversion Principle (DIP) Interface-Segregation Principle (ISP) The Interface-Segregation Principle (ISP) Interfaces provide an abstract way for two classes to communicate. However, an interface that provides different groups of methods to different clients is bad design. This is really just the Single Responsibility Principle applied to interfaces. The point is to avoid fat interfaces, where there is little cohesion between methods.

e.g. Secure Doors Assume that we wish to represent different kinds of doors. We define class Door: abstract class Door { public abstract void lock (); public abstract void unlock (); public abstract boolean isopen ();

We wish to create a concrete implementation of Door, called TimedDoor. A TimedDoor will sound an alarm if it has been left unlocked for too long. We decide to delegate the responsibility of managing the timing to a Timer class (application of the SRP). A TimerClient is also defined as an interface for classes who use Timer (application of the DIP): class Timer { public void register( int timeout, TimerClient client) { // Trigger an internal timer that will call // client. timeout () after timeout seconds has // elapsed. interface TimerClient { void timeout (); TimedDoor extends Door. But who implements TimerClient?

Choice 1: Door implements TimerClient. abstract class Door implements TimerClient { public abstract void lock (); public abstract void unlock (); public abstract boolean isopen (); public void timeout () {/ / This is bad. NotallDoorvariantswillneedbuilt-intiming.

Choice 2: TimedDoor implements TimerClient: class TimedDoor extends Door implements TimerClient { @Override public void lock () {/ / @Override public void unlock () {/ / @Override public boolean isopen () {/ / return true ; @Override public void timeout () {/ / This is much better! Doors are not polluted with timing-related stuff. It may be cleaner again to make Door an interface (this may not be possible if there is common implementation for all Doors).