Professional Documents
Culture Documents
2. Interface specification
Describes precisely each class interface Transforms the object design model to improve its understandability and extensibility Transforms the object design model to address performance criteria such as response time or memory utilization.
Select Subsystem
Specification
Reuse
Identifying components
Adjusting components Specifying types & signatures Identifying patterns Specifying constraints
Specifying exceptions
Adjusting patterns
Optimization Optimizing access paths Caching complex computations Delaying complex computations
Realizing associations
class diagrams can be used to model both the application domain and the solution domain. Application objects, also called domain objects, represent concepts of the domain that the system manipulates. Solution objects represent support components that do not have a counterpart in the application domain, such as persistent data stores, user interface objects, or middleware. During analysis, we identify application objects, their relationships, and attributes and operations. During system design, we identify subsystems and most important solution objects. During object design, we refine and detail both sets of objects and identify any remaining solution objects needed to complete the system.
Inheritance
Analysis activity
Taxonomy Inheritance for Reuse
Object Design
Specification Inheritance
Implementation Inheritance
Interface Specification
Used during requirements analysis Activity: identify application domain objects that are hierarchically related Goal: make the analysis model more understandable Used during object design Activity: identify the signatures of all identified objects Goal: increase reusability, enhance modifiability and extensibility
Superclass:
public class Car { public void drive() {} public void brake() {} public void accelerate() {} }
Subclass:
LuxuryCar
public class LuxuryCar extends Car { public void playMusic() {} public void ejectCD() {} public void resumeMusic() {} public void pauseMusic() {} }
Implementation inheritance
Also called class inheritance Goal:
Extend an applications functionality by reusing functionality from the super class Inherit from an existing class with some or all operations already implemented
Specification Inheritance
Also called sub typing Goal:
Inherit from a specification The specification is an abstract class with all operations specified, but not yet implemented.
A very similar class is already implemented that does almost the same as the desired class implementation List
Add() Remove() Already implemented
Example: I have a List class, I need a Stack class How about subclassing the Stack class from the List class and implementing Push(), Pop(), Top() with Add() and Remove()?
Stack
Inheritance: Extending a Base class by a new operation or overwriting an operation. Delegation: Catching an operation and sending it to another object.
List
+Add() +Remove()
Stack
+Push() +Pop() +Top()
List
Add() Remove()
Inheritance
Stack
+Push() +Pop() +Top()
Delegation
Delegation is a way of making composition as powerful for reuse as inheritance In delegation two objects are involved in handling a request from a Client
The Receiver object delegates operations to
the Delegate object The Receiver object makes sure, that the Client does not misuse the Delegate object.
Client Receiver delegates to
calls
Delegate
A class is said to delegate to another class if it implements an operation by merely resending a message to another class. Delegation makes clear the dependencies between the reused class and the new class.
Hashtable
put (key, element) get (key) :object J containsKey (key):boolean containsValue (element):boolean
Myset
/* Constructor omitted */
MySet() { } void put (Object element) { if (!containsKey (element)) { put (element, thiS); }} boolean containsValue (Object element) { return containsKey (element); }
Hashtable
put (key, element) get (key) :object J containsKey (key):boolean containsValue (element):boolean
Myset
/* Constructor omitted */
MySet() { table= hashtable(); } void put (Object element) { if (!containsKey (element)) { Table.put (element, thiS); }} boolean containsValue (Object element) { (return table.containsKey (element)); }
FUNCTIONS
THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT
The Liskov Substitution Principle is a way of ensuring that inheritance is used correctly. If a function does not satisfy the LSP, then it probably makes explicit reference to some or all of the subclasses of its superclass. Such a function also violates the Open-Closed Principle, since it may have to be modified whenever a new subclass is created.
Most people would think that a Square is a special kind of Rectangle and there for intuitively inherit their Square class from the Rectangle class to be able to reuse code. However if you study the given image you will notice that the Rectangle have a couple of traits that the Square does NOT and vice versa. First of all the Square has only *ONE* attribute; size, while the Rectangle have *TWO* attributes; width & height.
22
public class Rectangle { private double width; private double height; public Rectangle(double w, double h) { width = w; height = h; } public double getWidth() {return width;} public double getHeight() {return height;} public void setWidth(double w) {width = w;} public void setHeight(double h) {height = h;} public double area() {return (width * height); }
Now, how about a Square class? Clearly, a square is a rectangle, so the Square class should be derived from the Rectangle class
Observations:
A square does not need both a width and a height as attributes, but it will inherit them from Rectangle anyway. So, each Square object wastes a little memory, but this is not a major concern. The inherited setWidth() , setHeight() methods are not really appropriate for a Square, since the width and height of a square are identical.
// A Square class. public class Square extends Rectangle { public Square(double s) {super(s, s); } public void setWidth(double w) { super.setWidth(w); super.setHeight(w); } public void setHeight(double h) { super.setHeight(h); super.setWidth(h); } }
public class TestRectangle { // Define a method that takes a Rectangle reference. public static void testLSP(Rectangle r) { r.setWidth(4.0); r.setHeight(5.0); System.out.println("Width is 4.0 and Height is 5.0" + ", so Area is " + r.area()); if (r.area() == 20.0) System.out.println(OK!\n"); else System.out.println(Error!\n"); }
public static void main(String args[ ]) { //Create a Rectangle and a Square Rectangle r = new Rectangle(1.0, 1.0); Square s = new Square(1.0); // Now call the method above. According to the // LSP, it should work for either Rectangles or // Squares. Does it?? testLSP(r); testLSP(s); } }
Test program output: Width is 4.0 and Height is 5.0, so Area is 20.0 OK! Width is 4.0 and Height is 5.0, so Area is 25.0 Error! Looks like we violated the LSP!
A mathematical square might be a rectangle, but a Square object is not a Rectangle object. Behavior of a Square object is not consistent with the behavior of a Rectangle object! Behaviorally, a Square is not a Rectangle! A Square object is not polymorphic with a Rectangle object. If you inherit your Square class from your Rectangle class, then you might currently get around it by hiding the width property of the Rectangle somehow, maybe by convention, and then just let the size property of the Square forward the call to height or something similar, but this is always wrong! Having to override these simple methods is a clue that this might not be an appropriate use of inheritance!
The problem is when you hit "real life scenarios", especially with Single Inheritance programming languages - like for instance C# and Java.
Because if you're not supposed to break LSP you will often tend to repeat code a LOT. In C# you cannot inherit from multiple classes like you can in C and other MI (Multiple Inheritance languages) you cannot create "small, lightweight carrier classes" for you to inherit from to not break LSP !
30
Delegation
Inheritance
Flexibility: Any object can be replaced at run time by another one (as long as it has the same type Inefficiency: Objects are encapsulated. Straightforward to use Supported by many programming languages Easy to implement new functionality Inheritance exposes a subclass to the details of its parent class Any change in the parent class implementation forces the subclass to change (which requires recompilation of both)
Abstract method:
A method with a signature but without an implementation (also called abstract operation)
Abstract class:
A class which contains at least one abstract method is called abstract class
dispenseItem() must be implemented in each subclass. We do this by specifying the operation as abstract. Abstract operations are written in UML in italics.
Strict inheritance
The subclass can only add new methods to the superclass, it cannot over write them
If a method cannot be overwritten in a Java program, it must be prefixed with the keyword final.
Superclass:
public class Car { public final void drive() {} public final void brake() {} public final void accelerate() {} }
Subclass:
LuxuryCar
public class LuxuryCar extends Car { public void playMusic() {} public void ejectCD() {} public void resumeMusic() {} public void pauseMusic() {} }
Original Java-Code:
class Device { int serialnr; public final void help() {.} public void setSerialNr(int n) { serialnr = n; } } class Valve extends Device { Position s; public void on() { . } }
setSerialNr() overwritable
Original Java-Code:
class Device { int serialnr; public final void help() {.} public void setSerialNr(int n) { serialnr = n; } } class Valve extends Device { Position s; public void on() { . } }
New Java-Code :
class Device { int serialnr; public final void help() {.} public void setSerialNr(int n) { serialnr = n; } } class Valve extends Device { Position s; public void on() { } public void setSerialNr(int n) { serialnr = n + s.serialnr; } } // class Valve
What are Design Patterns? A design pattern describes a problem which occurs over and over again in our environment Then it describes the core of the solution to that problem, in such a way that you can use the this solution a million times over, without ever doing it the same twice Design pattern has four elements:1) Name that uniquely identifies the pattern from other patterns 2) Problem description : describes the situation in which the pattern can be used. 3) Solution: stated as a set of collaboration classes and interfaces. 4) Consequences: describes the trade-off and alternatives to be considered with respect to design goals.
This pattern decouples the interface of a class from its implementation. Also know as a Handle/Body pattern Allows different implementations of an interface to be decided upon dynamically.
Description
we need to decouple an Abstraction from an Implementor, because we need to substitute different Implementors for a given Abstraction without any impact on a calling Subsystem. This is realized by providing an Abstraction class that implements its services in terms of the methods of an Implementor interface. Concrete Implementors that need to be substituted refine the Implementor interface.
It provides a bridge between the Abstraction (in the application domain) and the Implementor (in the solution domain)
Decouples an abstraction from its implementation so that the two can vary independently This allows to bind one from many different implementations of an interface to a client dynamically Design decision that can be realized any time during the runtime of the system
The bridge pattern can be used to provide multiple implementations under the same interface
Interface to a component that is incomplete (only Stub code is available), not yet known or unavailable during testing If seat data are required to be read, but the seat is not yet implemented (only stub code available), or only available by a simulation (AIM or SART), the bridge pattern can be used:
VIP Seat (in Vehicle Subsystem) GetPosition() SetPosition()
imp
SeatImplementation
Stub Code
AIMSeat
SARTSeat
Delegation
LeagueStoreImplementor
Inheritance
Also known as a wrapper. Uses both inheritance and delegation. The adapter pattern lets classes work together that couldnt otherwise because of incompatible interfaces
It converts the interface of one component into another interface expected by the other (calling) component Used to provide a new interface to existing legacy components (Interface engineering, reengineering)
ClientInterface Request()
LegacyClass ExistingRequest()
Client
ClientInterface Request()
LegacyClass ExistingRequest()
adaptee
Inheritance
Adapter
Request()
Delegation
The adapter pattern uses inheritance as well as delegation: - Interface inheritance is used to specify the interface of the Adapter class. - Delegation is used to bind the Adapter and the Adaptee
Object adapter:
Uses single inheritance and delegation
Object adapters are much more frequent. We cover only object adapters (and call them adapters).
Examples of tasks:
Different collision strategies for objects in video games Parsing a set of tokens into an abstract syntax tree (Bottom up, top down) Sorting a list of customers (Bubble sort, mergesort, quicksort)
If we need a new algorithm, we can add it without disturbing the application or the other algorithms.
Policy
Context
ContextInterface()
Strategy
AlgorithmInterface
ConcreteStrategyA
AlgorithmInterface()
ConcreteStrategyB
AlgorithmInterface()
ConcreteStrategyC
AlgorithmInterface()
Client
SortInterface
Sort()
BubbleSort
Sort()
QuickSort
Sort()
MergeSort
Sort()
policy
Mobile Application
Client class
AbstractProductA
ProductA1
ProductA2
ConcreteFactory1
AbstractProductB
CreateProductA CreateProductB
ProductB1 ConcreteFactory2 CreateProductA CreateProductB ProductB2
Initiation Assocation: Class ConcreteFactory2 initiates the associated classes ProductB2 and ProductA2
Independence from Initialization or Representation or platform Manufacturer Independence Constraints on related products Cope with upcoming change
Abstract Factory offers the interface for creating a family of related objects
IntelligentHouse
LightBulb
Blind
EIBBulb
LuxmateBulb
EIBBlind
LuxmateBlind
You want to build a user interface You want to provide menus You want to make the menus reusable across many applications
The applications only know what has to be done when a command from the menu is selected You dont want to hardcode the menu commands for the various applications
Such a user interface can easily be implemented with the Command Pattern.
Command
Invoker Client Receiver action1() action2()
binds ConcreteCommand1
execute()
binds
execute()
ConcreteCommand2
execute()
Client (in this case a user interface builder) creates a ConcreteCommand and binds it to an action operation in Receiver Client hands the ConcreteCommand over to the Invoker which stores it (for example in a menu) The Invoker has the responsibility to execute or undo a command (based on a string entered by the user)
The Command abstract class declares the interface supported by all ConcreteCommands. The client is a class in a user interface builder or in a class executing during startup of the application to build the user interface. The client creates concreteCommands and binds them to specific Receivers, this can be strings like commit, execute, undo. So all user-visible commands are sub classes of the Command abstract class. The invoker - the class in the application program offering the menu of commands or buttons - invokes theconcreteCommand based on the string entered and the binding between action and ConcreteCommand.
The command pattern can be nicely used to decouple boundary objects from control objects:
Boundary objects such as menu items and buttons, send messages to the command objects (I.e. the control objects) Only the command objects modify entity objects
When the user interface is changed (for example, a menu bar is replaced by a tool bar), only the boundary objects are modified.
Move execute()
execute()
ChessMove execute()
Models tree structures that represent part-whole hierarchies with arbitrary depth and width. The Composite Pattern lets client treat individual objects and compositions of these objects uniformly There are times when a program needs to manipulate a tree data structure and it is necessary to treat both Branches as well as Leaf Nodes uniformly. Eg File System.
Client
Component
stores child components in addition to implementing methods defined by the component interface.
Leaf
Operation()
Composite
Operation() AddComponent RemoveComponent() GetChild()
Children
Software System:
Definition: A software system consists of subsystems which are either other subsystems or collection of classes Composite: Subsystem (A software system consists of subsystems which consists of subsystems , which consists of subsystems, which...) Leaf node: Class
User
Software System
Class Subsystem
Children
The Graphic Class represents both primitives (Line, Circle) and their containers (Picture)
Client Graphic
Line
Draw()
Circle
Draw()
Picture
Draw() Add(Graphic g) RemoveGraphic) GetChild(int)
Children
Similarities:
Difference:
The adapter pattern is geared towards making unrelated components work together
Applied to systems after theyre designed (reengineering, interface engineering). Inheritance followed by delegation
A bridge, on the other hand, is used up-front in a design to let abstractions and implementations vary independently.
Green field engineering of an extensible system New beasts can be added to the object zoo, even if these are not known at analysis or system design time. Delegation followed by inheritance
A framework is a reusable partial application that can be specialized to produce custom applications. The key benefits of frameworks are reusability and extensibility:
Reusability leverages of the application domain knowledge and prior effort of experienced developers Extensibility is provided by hook methods, which are overwritten by the application to extend the framework.
Frameworks are targeted to particular technologies, such as data processing or cellular communications, or to application domains, such as user interfaces or real-time avionics. Frameworks uses Hook methods . Hook methods systematically decouple the interfaces and behaviors of an application domain from the variations required by an application in a particular context.
White-box frameworks:
Black-box frameworks:
Extensibility achieved through inheritance and dynamic binding. Existing functionality is extended by subclassing framework base classes and overriding specific methods (so-called hook methods) Extensibility achieved by defining interfaces for components that can be plugged into the framework. Existing functionality is reused by defining components that conform to a particular interface These components are integrated with the framework via delegation.
Class Library:
Provide a smaller scope of reuse Less domain specific Class libraries are passive; no constraint on the flow of control
Framework:
Classes cooperate for a family of related applications. Frameworks are active; they affect the flow of control.
Components:
Self-contained instances of classes Plugged together to form complete applications Can even be reused on the binary code level
The advantage is that applications do not have to be recompiled when components change
Framework:
Often used to develop components Components are often plugged into blackbox frameworks.
WebBrowser
WebObjects
WebServer StaticHTML
RelationalDatabase