SOFTWARE ENGINEERING

Lecture 15 Design Patterns 1

MBA Course Notes Dr. ANH DAO NAM

1

Software Engineering

Slides are from Ivan Marsic and Thomas E. Potok, modified by Anh Dao Nam Textbooks:

(cid:1) Bruegge & Dutoit: Object-Oriented Software Engineering: Using UML,

Patterns and Java, Third Edition, Prentice Hall, 2010.

(cid:1) Miles & Hamilton: Learning UML 2.0, O’Reilly Media, 2006.

Some interesting books for the advanced material include:

(cid:1) R. Pressman, Software Engineering - A Practitioner's Approach, 6th ed.,

2005

(cid:1) C. Ghezzi, M. Jazayeri, and D. Mandriolo, Fundamentals of Software

Engineering. Prentice Hall, second ed., 2002

(cid:1) A. Endres and D. Rombach, A Handbook of Software and Systems Engineering. The Fraunhofer IESE Series on Software Engineering, Pearson Education Ltd., 2003.

(cid:1) S. Robertson and J. C. Robertson, Mastering the Requirements Process.

Addison-Wesley Professional, second ed., 2006.

(cid:1) I. Jacobson, G. Booch, and J. Rumbaugh, The Unified Software Development Process. Addison-Wesley Professional, 1999.

(cid:1) K. Beck and C. Andres, Extreme Programming Explained. Addison-Wesley,

2004.

2

Topics

Software Design Patterns – What & Why

Example Pattern: Publisher-Subscriber (a.k.a. Observer)

3

What Developers Do With Software

(besides development)

Understand Maintain (fix bugs) Upgrade (add new features)

4

The Power of Patterns

NIB

CN

MAP

PLE

5

The Power of Patterns

6

The Power of Patterns

APPLE

IBM

CNN

7

The Power of Patterns

8

The Power of Patterns

NIB

CN

MAP

PLE

APPLE

IBM

CNN

9

Software Design Patterns

Design Patterns help anticipate change Change is bad when there are unrelated reasons to change a software module/class (cid:1) Unrelated reasons are usually because of

unrelated responsibilities

Another target is complex conditional logic (If-Then-Else statements, etc.)

10

What May Change & How

What changes in real world are business rules (cid:2) customer requests changes in software Changes in the small—changes in object responsibilities towards other objects (cid:1) Number of responsibilities (cid:1) Data type, or method signature (cid:1) Business rules (cid:1) Conditions for provision/fulfillment of

responsibilities

Sometimes change is regular (follows simple rules), such as new object state defined, or another object needs to be notified about something

11

Object Responsibilities (toward other objects)

Knowing something (memorization of data or object attributes) Doing something on its own (computation programmed in a “method”) (cid:1) Business rules for implementing business

policies and procedures

Calling methods of other objects (communication by sending messages) (cid:1) Calling constructor methods; this is special because the caller must know the appropriate parameters for initialization of the new object.

12

Patterns for Tackling Responsibilities

Delegating “knowing” and associated “doing” responsibilities (State) Delegating “calling” responsibilities (Command, Publisher-Subscriber) Delegating non-essential “doing” responsibilities (when “doing” responsibility incrementally modified) (Decorator)

13

Key Issues

When a pattern is needed/applicable? How to measure if a pattern-based solution is better? When to avoid patterns because may make things worse? (cid:3) All of the above should be answered in terms of object responsibilities before/after a pattern is applied

14

Publisher-Subscriber Pattern

A.k.a. “Observer”

Disassociates unrelated responsibilities

T

F

?

Helps simplify/remove complex conditional logic and allow seamless future adding of new cases

Based on “Indirect Communication”

15

Request- vs. Event-Based Comm.

Request: doSomething( info )

Request: getInfo()

(1) Request: subscribe()

info

Client

Server

Doer

Doer

Info Src

Info Src

(2) event( info )

(a)

(b)

(c)

Direct Communication

Indirect Comm.

16

“Before” == A Scenario Suitable for Applying the Pub-Sub Pattern

Event Detector

DoerType1

DoerType2

detectEvent() …

tellMeWhatToDo() …

tellMeWhatToDo() …

Doer1.tellMeWhatToDo() Doer2.tellMeWhatToDo() …

Responsibilities Doing:

• Detect events

Calling: • •

Tell Doer-1 what to do Tell Doer-2 what to do

unrelated! ⇒ unrelated reasons to change the Event Detector: • When event detection needs to change or extend • When new doer types need to be told what to do

17

“After” == Responsibilities After Applying the Pub-Sub Pattern

Subscriber

Publisher subscribers : List …

detectEvent() …

receiveEvent() …

subscriber.receive(Event) …

Unrelated responsibilities of the Event Detector (now Publisher) are dissociated:

• When event detection needs to change or extend → change Publisher • When new doer types need to be added → add an new Subscriber type

(Subscribers need not be told what to do – they know what to do when a given event occurs!

18

Publisher-Subscriber Pattern

Focused on classifying events (cid:1) The focus in on the “Publisher” object and

the “environment” that it is observing

(cid:1) Example key events in safe home access:

(cid:1) Entered key is valid (cid:1) Entered key is invalid

Instead of making decisions & issuing orders to Doers/Subscribers (cid:1) Focusing on other objects’ work: deciding

when it is appropriate to call each one

19

Publisher-Subscriber Pattern

PUBLISHER

Controller (receives key-code) Key Checker (checks validity, i.e., classifies: valid/invalid) (cid:1) Publishes the classification result to

“subscribers”

SUBSCRIBERS

Lock Control Light Control Alarm Control (cid:1) Do the work based on the event

classification outcome

( Assumed: subscription step first )

20

Pub-Sub Pattern

Publisher

Knowing Responsibilities:

• Knows event source(s) • Knows interested obj’s (subscribers)

Doing Responsibilities:

«interface» Publisher

*

• Registers/Unregisters subscribers • Notifies the subscribers of events

«interface» Subscriber

subscribers

+ receive()

+ subscribe() + unsubscribe()

Subscriber

Knowing Responsibilities:

Type1Publisher

Type2Publisher

Type1Subscriber

+ receive()

+ subscribe() + unsubscribe()

+ subscribe() + unsubscribe()

• Knows event types of interest • Knows publisher(s) Doing Responsibilities:

• Registers/Unregisters with publishers • Processes received event notifications

(a)

(b)

21

From Chapter 2

Unlock Use Case

enterKey()

k := create()

loop

[for all stored keys]

val := checkKey(k)

sk := getNext()

compare()

logTransaction(k, val)

«destroy»

alt

val == true

activate(“lock”)

dl := isDaylight()

dl == false

opt

activate(“bulb”)

numOfAttempts++

[else]

numOfAttempts == maxNumOfAttempts

alt

denyMoreAttempts()

activate(“alarm”)

[else]

prompt: "try again"

22

: Controller k : Key : Checker : KeyStorage : DeviceCtrl : PhotoObsrv : Logger

Refactoring to Publisher-Subscriber

Conditional logic is decided here, at design time, instead of run time

1. Subscribe for appropriate events

Subscriber

Event type

Pub

Sub-1

LockCtrl, LightCtrl

keyIsValid

Sub-n

AlarmCtrl

keyIsInvalid

LightCtrl

itIsDarkInside

. . .

Sub-n

Pub

Sub-1

No need to consider the “appropriateness” of calling the “servers”

2. When event occurs:

Design-time decisions are better than runtime decisions, because they can be easier checked if they “work” (before the product is developed)

(a) Classify: keyIsValid / keyIsInvalid (b) Notify only the subscribers for the detected event class

If a new device is added -- just write a new class; NO modification of the Publisher!

23

Pub-Sub: Unlock Use Case

: KeyStorage : KeyStorage : PhotoSObs : PhotoSObs : AlarmCtrl : AlarmCtrl : Checker : Checker : LightCtrl : LightCtrl : LockCtrl : LockCtrl k : Key k : Key : Controller : Controller : Logger : Logger

enterKey() enterKey() k := create() k := create()

loop loop checkKey(k) checkKey(k) sk := getNext() sk := getNext()

compare() compare()

alt alt valid == true valid == true for all KeyIsValid subscribers for all KeyIsValid subscribers loop loop keyIsValid() keyIsValid()

keyIsValid() keyIsValid()

keyIsValid() keyIsValid()

dl := isDaylight() dl := isDaylight()

dl == false dl == false opt opt

setLit(true) setLit(true)

[else] [else] for all KeyIsInvalid subscribers for all KeyIsInvalid subscribers loop loop keyIsInvalid() keyIsInvalid() numOfAttempts++ numOfAttempts++

keyIsInvalid() keyIsInvalid() prompt: prompt: "try again" "try again" numOfAttempts == maxNumOfAttempts numOfAttempts == maxNumOfAttempts opt opt

soundAlarm() soundAlarm()

24

keyIsInvalid() keyIsInvalid()

From Hub-and-Spokes (Star) to Token Passing (Cascading) Architecture

: Key : KeyStorage

Before:

getNext()

: Checker create()

checkKey()

After:

: Logger

setOpen() : Controller logTransaction()

: LockCtrl

isDaylight()

: Key getNext() : Checker setLit() create() soundAlarm()

(a)

: AlarmCtrl : KeyStorage : PhotoSObs checkKey() : LightCtrl : Controller keyIsValid() keyIsInvalid()

: LockCtrl : AlarmCtrl

Pub-Sub reduced the class coupling:

: Logger : LightCtrl

isDaylight()

25

(b)

: PhotoSObs

Design Patterns: Delegation

initializes the pattern

Custodian

Instantiation of the Design Pattern

Client

asks for service

collection of objects working to provide service

Alternative names for Custodian are Assembler or Initializer

26

Pub-Sub: Initialization

: Controller : Controller

: Checker : Checker

: LockCtrl : LockCtrl

: LightCtrl : LightCtrl

: AlarmCtrl : AlarmCtrl

: Logger : Logger

create() create()

subscribeKeyIsInvalid() subscribeKeyIsInvalid()

subscribeKeyIsValid() subscribeKeyIsValid()

A method call A method call that passes a that passes a reference to the reference to the Checker Checker

subscribeKeyIsValid() subscribeKeyIsValid()

subscribeKeyIsInvalid() subscribeKeyIsInvalid()

subscribeKeyIsValid() subscribeKeyIsValid()

subscribeKeyIsInvalid() subscribeKeyIsInvalid()

27

Practical Issues

1. Do not design for patterns first

(cid:1) Reaching any kind of solution is the

priority; solution optimization should be secondary

2. Refactor the solution to patterns

(cid:1) E.g., to reduce the complexity of the

program’s conditional logic

Uncritical use of patterns may yield worse solutions!

28

Q&A

29