ECE 355 Software Engineering CHAPTER 8 Instructor Kostas

  • Slides: 40
Download presentation
ECE 355: Software Engineering CHAPTER 8 Instructor Kostas Kontogiannis 1

ECE 355: Software Engineering CHAPTER 8 Instructor Kostas Kontogiannis 1

Course outline • Unit 1: Software Engineering Basics • Unit 2: Process Models and

Course outline • Unit 1: Software Engineering Basics • Unit 2: Process Models and Software Life Cycles • Unit 3: Software Requirements • Unit 4: Unified Modeling Language (UML) • Unit 5: Design Basics and Software Architecture • Unit 6: OO Analysis and Design Unit 7: Design Patterns • Unit 8: Testing and Reliability • Unit 9: Software Engineering Management and Economics 2

References • The material for this lecture was obtained from – http: //www. dofactory.

References • The material for this lecture was obtained from – http: //www. dofactory. com/Patterns. aspx#list – http: //www. developer. com/design/article. php/1502691 3

Adapter Design Pattern • The Adapter is intended to provide a way for a

Adapter Design Pattern • The Adapter is intended to provide a way for a client to use an object whose interface is different from the one expected by the client, without having to modify either • This pattern is suitable for solving issues that arise, for example, when: – 1) you want to replace one class with another and the interfaces do not match, and – 2) you want to create a class that can interact with other classes without knowing their interfaces at design time 5

Adapter Design Pattern - Operation • First, you create a custom Target class that

Adapter Design Pattern - Operation • First, you create a custom Target class that defines methods using an interface as expected by the client • Second, you create a custom Adapter that implements the interface expected by the Adaptee. The Adapter class subclasses the Target, and provides an alternate implementation of the client requests in terms that the Adaptee expects. Adapter overrides the Target method and provides the correct interface for Adaptee. • This approach has the advantage that it does not lead to modifications in the client. Adapter is a structural pattern and you can use it to react to changes in class interfaces as your system evolves, or you can proactively use the Adapter pattern to build systems that anticipate changing structural details 6

Adapter Design Pattern 7

Adapter Design Pattern 7

Adapter Design Pattern • Participants: The classes and/or objects participating in this pattern are:

Adapter Design Pattern • Participants: The classes and/or objects participating in this pattern are: • Target – defines the domain-specific interface that Client uses. • Adapter – adapts the interface Adaptee to the Target interface. • Adaptee – defines an existing interface that needs adapting. • Client – collaborates with objects conforming to the Target interface. 8

Adapter Design Pattern // "Target" // "Adaptee" class Target { public virtual void Request()

Adapter Design Pattern // "Target" // "Adaptee" class Target { public virtual void Request() { Console. Write. Line("Called Target Request()"); } } class Adaptee { public void Specific. Request() { Console. Write. Line("Called Specific. Request()"); } } class Adapter : Target { private Adaptee adaptee = new Adaptee(); } public override void Request() { // Possibly do some other work // and then call Specific. Request adaptee. Specific. Request(); } Output Called Specific. Request() } // Client class Main. App { static void Main() { // Create adapter and place a request Target target = new Adapter(); target. Request(); } } // Wait for user Console. Read(); 9

Bridge Design Pattern - Operation • Decouple an abstraction from its implementation so that

Bridge Design Pattern - Operation • Decouple an abstraction from its implementation so that the two can vary independently • The Bridge pattern is useful when there is a hierarchy of abstractions and a corresponding hierarchy of implementations. Rather than combining the abstractions and implementations into many distinct classes, the Bridge pattern implements the abstractions and implementations as independent classes that can be combined dynamically. Related patterns are : – Layered Architecture The Bridge design pattern is a way of organizing the entities identified using the Layered Architecture pattern into classes. – Abstract Factory The Abstract Factory pattern can be used by the Bridge pattern to decide which implementation class to instantiate for an abstraction object 10

Bridge Design Pattern • Abstraction – defines the abstraction's interface. – maintains a reference

Bridge Design Pattern • Abstraction – defines the abstraction's interface. – maintains a reference to an object of type Implementor. • Refined. Abstraction – extends the interface defined by Abstraction. • Implementor – defines the interface for implementation classes. This interface doesn't have to correspond exactly to Abstraction's interface; in fact the two interfaces can be quite different. Typically the Implementation interface provides only primitive operations, and Abstraction defines higher-level operations based on these primitives. • Concrete. Implementor – implements the Implementor interface and defines its concrete implementation. 11

Bridge Design Pattern 12

Bridge Design Pattern 12

Bridge Design Pattern // "Abstraction" class Abstraction { protected Implementor implementor; // Property public

Bridge Design Pattern // "Abstraction" class Abstraction { protected Implementor implementor; // Property public Implementor { set{ implementor = value; } } } public virtual void Operation() { implementor. Operation(); } // "Implementor" abstract class Implementor { public abstract void Operation(); } // "Refined. Abstraction" class Refined. Abstraction : Abstraction { public override void Operation() { implementor. Operation(); } } 13

Bridge Design Pattern // "Concrete. Implementor. A" class Concrete. Implementor. A : Implementor {

Bridge Design Pattern // "Concrete. Implementor. A" class Concrete. Implementor. A : Implementor { public override void Operation() { Console. Write. Line("Concrete. Implementor. A Operation"); } } // "Concrete. Implementor. B" class Concrete. Implementor. B : Implementor { public override void Operation() { Console. Write. Line("Concrete. Implementor. B Operation"); } } 14

Bridge Design Pattern - Client class Main. App { static void Main() { Abstraction

Bridge Design Pattern - Client class Main. App { static void Main() { Abstraction ab = new Refined. Abstraction(); // Set implementation and call ab. Implementor = new Concrete. Implementor. A(); ab. Operation(); // Change implemention and call ab. Implementor = new Concrete. Implementor. B(); ab. Operation(); } } // Wait for user Console. Read(); Output Concrete. Implementor. A Operation Concrete. Implementor. B Operation 15

State Design Pattern - Operation • Allow an object to alter its behavior when

State Design Pattern - Operation • Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. • The State pattern is useful when you want to have an object represent the state of an application, and you want to change the state by changing that object. • The State pattern is intended to provide a mechanism to allow an object to alter its behavior in response to internal state changes. To the client, it appears as though the object has changed its class. • The benefit of the State pattern is that state-specific logic is localized in classes that represent that state. 16

State Design Pattern Participants: The classes and/or objects participating in this pattern are: •

State Design Pattern Participants: The classes and/or objects participating in this pattern are: • Context – defines the interface of interest to clients – maintains an instance of a Concrete. State subclass that defines the current state. • State – defines an interface for encapsulating the behavior associated with a particular state of the Context. • Concrete State – each subclass implements a behavior associated with a state of Context 17

State Design Pattern 18

State Design Pattern 18

State Design Pattern // "State" abstract class State { public abstract void Handle(Context context);

State Design Pattern // "State" abstract class State { public abstract void Handle(Context context); } class Context { private State state; public Context(State Astate) { this. state = Astate; } // "Concrete. State. A" class Concrete. State. A : State { public override void Handle(Context context) { context. State = new Concrete. State. B(); } } // Property public State { get{ return state; } set { state = value; Console. Write. Line("State: " + state. Get. Type(). Name); } } // "Concrete. State. B" class Concrete. State. B : State { public override void Handle(Context context) { context. State = new Concrete. State. A(); } } public void Request() { state. Handle(this); } } 19

State Design Pattern - Client static void Main() { // Setup context in a

State Design Pattern - Client static void Main() { // Setup context in a state Context c = new Context(new Concrete. State. A()); // Issue requests, which toggles state c. Request(); } } // Wait for user Console. Read(); Output State: Concrete. State. A State: Concrete. State. B State: Concrete. State. A 20

Factory Method - Operation • When developing classes, you always provide constructors for your

Factory Method - Operation • When developing classes, you always provide constructors for your clients' use. There are certain circumstances in which you do not want your clients to know which class out of several to instantiate. • The Factory Method is intended to define an interface for clients to use to create an object, but lets subclasses decide which class to instantiate. Factory Methods let a class defer instantiation to subclasses. 21

Factory Method • The classes and/or objects participating in this pattern are: • Product

Factory Method • The classes and/or objects participating in this pattern are: • Product – defines the interface of objects the factory method creates • Concrete. Product – implements the Product interface • Creator – declares the factory method, which returns an object of type Product. Creator may also define a default implementation of the factory method that returns a default Concrete. Product object. – may call the factory method to create a Product object. • Concrete. Creator – overrides the factory method to return an instance of a Concrete. Product. 22

Factory Method 23

Factory Method 23

Factory Method abstract class Product { } // "Concrete. Creator" class Concrete. Creator. A

Factory Method abstract class Product { } // "Concrete. Creator" class Concrete. Creator. A : Creator { public override Product Factory. Method() { return new Concrete. Product. A(); } } // "Concrete. Product. A" class Concrete. Product. A : Product { } // "Concrete. Product. B" class Concrete. Product. B : Product { } // "Concrete. Creator" class Concrete. Creator. B : Creator { public override Product Factory. Method() { return new Concrete. Product. B(); } } // "Creator" abstract class Creator { public abstract Product Factory. Method(); } } 24

Factory Method - Client class Main. App { static void Main() { // An

Factory Method - Client class Main. App { static void Main() { // An array of creators Creator[] creators = new Creator[2]; creators[0] = new Concrete. Creator. A(); creators[1] = new Concrete. Creator. B(); // Iterate over creators and create products { } } } foreach(Creator creator in creators) Product product = creator. Factory. Method(); Console. Write. Line("Created {0}", product. Get. Type(). Name); // Wait for user Console. Read(); Output: Created Concrete. Product. A Created Concrete. Product. B 25

Command Design Pattern - Operation • The Command pattern is intended to encapsulate a

Command Design Pattern - Operation • The Command pattern is intended to encapsulate a request as an object. For example, consider a window application that needs to make requests of objects responsible for the user interface. • The client responds to input from the user by creating a command object and the receiver. It then passes this object to an Invoker object, which then takes the appropriate action. This allows the client to make requests without having to know anything about what action will take place. • In addition, you can change that action without affecting the client. Practical uses for Command are for creating queues and stacks for supporting "undo" operations, configuration changes, and so on. 26

Command Design Pattern Participants: The classes and/or objects participating in this pattern are: •

Command Design Pattern Participants: The classes and/or objects participating in this pattern are: • Command (e. g. Command) – declares an interface for executing an operation • Concrete. Command (e. g. Calculator. Command) – defines a binding between a Receiver object and an action – implements Execute by invoking the corresponding operation(s) on Receiver • Client (e. g. Command. App) – creates a Concrete. Command object and sets its receiver • Invoker (e. g. User) – asks the command to carry out the request • Receiver (e. g. Calculator) – knows how to perform the operations associated with carrying out the request 27

Command Design Pattern 28

Command Design Pattern 28

Command Design Pattern abstract class Command { protected Receiver receiver; // Constructor public Command(Receiver

Command Design Pattern abstract class Command { protected Receiver receiver; // Constructor public Command(Receiver receiver) { this. receiver = receiver; } } public abstract void Execute(); // "Concrete. Command" class Concrete. Command : Command { // Constructor public Concrete. Command(Receiver receiver) : base(receiver) { } } // "Receiver" class Receiver { public void Action() { Console. Write. Line("Called Receiver. Action()"); } } // "Invoker" class Invoker { private Command command; public void Set. Command(Command command) { this. command = command; } public override void Execute() { receiver. Action(); } public void Execute. Command() { command. Execute(); } } 29

Command Design Pattern - Client class Main. App { static void Main() { //

Command Design Pattern - Client class Main. App { static void Main() { // Create receiver, command, and invoker Receiver receiver = new Receiver(); Command command = new Concrete. Command(receiver); Invoker invoker = new Invoker(); // Set and execute command invoker. Set. Command(command); invoker. Execute. Command(); // Wait for user } } Output Called Receiver. Action() Console. Read(); 30

Proxy Design Pattern - Operation • Provide a surrogate or placeholder for another object

Proxy Design Pattern - Operation • Provide a surrogate or placeholder for another object to control access to it. • This pattern is useful for situations where object creation is a time consuming process and can make an application appear sluggish. A proxy object can provide the client with feedback while the object is being created. • Proxy can also be used to provide a more sophisticated reference to an object. For example, the proxy object can implement additional logic on the objects behalf (security, remote procedure calls, an so forth). 31

Proxy Design Pattern Participants: The classes and/or objects participating in this pattern are: •

Proxy Design Pattern Participants: The classes and/or objects participating in this pattern are: • Proxy (Math. Proxy) – maintains a reference that lets the proxy access the real subject. Proxy may refer to a Subject if the Real. Subject and Subject interfaces are the same. – provides an interface identical to Subject's so that a proxy can be substituted for the real subject. – controls access to the real subject and may be responsible for creating and deleting it. – other responsibilites depend on the kind of proxy: • remote proxies are responsible for encoding a request and its arguments and for sending the encoded request to the real subject in a different address space. • virtual proxies may cache additional information about the real subject so that they can postpone accessing it. For example, the Image. Proxy from the Motivation caches the real images's extent. • protection proxies check that the caller has the access permissions required to perform a request. • Subject (IMath) – defines the common interface for Real. Subject and Proxy so that a Proxy can be used anywhere a Real. Subject is expected. • Real. Subject (Math) – defines the real object that the proxy represents 32

Proxy Design Pattern 33

Proxy Design Pattern 33

Proxy Design Pattern // "Subject" abstract class Subject { public abstract void Request(); }

Proxy Design Pattern // "Subject" abstract class Subject { public abstract void Request(); } // "Proxy" class Proxy : Subject { Real. Subject real. Subject; // "Real. Subject" public override void Request() { // Use 'lazy initialization' if (real. Subject == null) { real. Subject = new Real. Subject(); } class Real. Subject : Subject { public override void Request() { Console. Write. Line("Called Real. Subject. Request()"); } } real. Subject. Request(); } } 34

Proxy Design Pattern - Client class Main. App { static void Main() { //

Proxy Design Pattern - Client class Main. App { static void Main() { // Create proxy and request a service Proxy proxy = new Proxy(); proxy. Request(); } } // Wait for user Console. Read(); Output Called Real. Subject. Request() 35

Chain of Responsibility Design Pattern • One of the tenets of software engineering is

Chain of Responsibility Design Pattern • One of the tenets of software engineering is to keep objects loosely coupled. The Chain of Responsibility is intended to promote loose coupling between the sender of a request and its receiver by giving more than one object an opportunity to handle the request. • The receiving objects are chained and pass the request along the chain until one of the objects handles it. • The result is to avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. 36

Chain of Responsibility Design Pattern • Participants: The classes and/or objects participating in this

Chain of Responsibility Design Pattern • Participants: The classes and/or objects participating in this pattern are: • Handler – defines an interface for handling the requests – (optional) implements the successor link • Concrete. Handler – handles requests it is responsible for – can access its successor – if the Concrete. Handler can handle the request, it does so; otherwise it forwards the request to its successor • Client – initiates the request to a Concrete. Handler object on the chain 37

Chain of Responsibility Design Pattern 38

Chain of Responsibility Design Pattern 38

Chain of Responsibility Design Pattern // "Handler" abstract class Handler { protected Handler successor;

Chain of Responsibility Design Pattern // "Handler" abstract class Handler { protected Handler successor; public void Set. Successor(Handler successor) { this. successor = successor; } public abstract void Handle. Request(int request); } // "Concrete. Handler 1" class Concrete. Handler 1 : Handler { public override void Handle. Request(int request) { if (request >= 0 && request < 10) { Console. Write. Line("{0} handled request {1}", this. Get. Type(). Name, request); } else if (successor != null) { successor. Handle. Request(request); } } } 39

Chain of Responsibility Design Pattern // "Concrete. Handler 2" class Concrete. Handler 2 :

Chain of Responsibility Design Pattern // "Concrete. Handler 2" class Concrete. Handler 2 : Handler { public override void Handle. Request(int request) { if (request >= 10 && request < 20) { Console. Write. Line("{0} handled request {1}", this. Get. Type(). Name, request); } else if (successor != null) { successor. Handle. Request(request); } } } // "Concrete. Handler 3" class Concrete. Handler 3 : Handler { public override void Handle. Request(int request) { if (request >= 20 && request < 30) { Console. Write. Line("{0} handled request {1}", this. Get. Type(). Name, request); } else if (successor != null) { successor. Handle. Request(request); } } } 40

Chain of Responsibility Design Pattern class Main. App { static void Main() { //

Chain of Responsibility Design Pattern class Main. App { static void Main() { // Setup Chain of Responsibility Handler h 1 = new Concrete. Handler 1(); Handler h 2 = new Concrete. Handler 2(); Handler h 3 = new Concrete. Handler 3(); h 1. Set. Successor(h 2); h 2. Set. Successor(h 3); // Generate and process request int[] requests = {2, 5, 14, 22, 18, 3, 27, 20}; foreach (int request in requests) { h 1. Handle. Request(request); } } } // Wait for user Console. Read(); Output Concrete. Handler 1 handled request 2 Concrete. Handler 1 handled request 5 Concrete. Handler 2 handled request 14 Concrete. Handler 3 handled request 22 Concrete. Handler 2 handled request 18 Concrete. Handler 1 handled request 3 Concrete. Handler 3 handled request 27 Concrete. Handler 3 handled request 20 41