0% found this document useful (0 votes)
35 views22 pages

SW D A Lab Manual 11

The document describes the Observer design pattern. The Observer pattern allows objects called subscribers to register with objects called publishers to receive notifications about any changes in state of the publisher. When a change occurs, the publisher notifies all registered subscribers. This avoids tight coupling between publishers and subscribers. The document provides an example of a text editor as a publisher and logging and email alert subscribers. It includes pseudocode demonstrating how the pattern can be implemented.

Uploaded by

IRFAN MUSTAFA
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views22 pages

SW D A Lab Manual 11

The document describes the Observer design pattern. The Observer pattern allows objects called subscribers to register with objects called publishers to receive notifications about any changes in state of the publisher. When a change occurs, the publisher notifies all registered subscribers. This avoids tight coupling between publishers and subscribers. The document provides an example of a text editor as a publisher and logging and email alert subscribers. It includes pseudocode demonstrating how the pattern can be implemented.

Uploaded by

IRFAN MUSTAFA
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 22

DHA SUFFA UNIVERSITY

Department of Computer Science

Software Design & Architecture


(SE-2403)

Spring 2022

SOFTWARE DESIGN PATTERNS


OBSERVER
Also known as: Event-Subscriber, Listener

Observer is a behavioral design pattern that lets you define a


subscription mechanism to notify multiple objects about any
events that happen to the object they’re observing.
 Problem
Imagine that you have two types of objects: Customer and
a
a Store . The customer is very interested in a particular brand
of product (say, it’s a new model of the iPhone) which should
become available in the store very soon.

The customer could visit the store every day and check prod-
uct availability. But while the product is still en route, most
of these trips would be pointless.

Visiting the store vs. sending spam

On the other hand, the store could send tons of emails (which
might be considered spam) to all customers each time a
new product becomes available. This would save some
customers from endless trips to the store. At the same
time, it’d upset other customers who aren’t interested in
new products.
It looks like we’ve got a conflict. Either the customer
wastes time checking product availability or the store
wastes resources notifying the wrong customers.

 Solution
The object that has some interesting state is often called
sub- ject, but since it’s also going to notify other objects
about the changes to its state, we’ll call it publisher. All other
objects that want to track changes to the publisher’s state are
called sub- scribers.

The Observer pattern suggests that you add a subscription


mechanism to the publisher class so individual objects can
subscribe to or unsubscribe from a stream of events
coming from that publisher. Fear not! Everything isn’t as
complicated as it sounds. In reality, this mechanism consists
of 1) an array field for storing a list of references to
subscriber objects and 2) several public methods which allow
adding subscribers to and removing them from that list.

A subscription mechanism lets individual objects subscribe to


event notifications.
Now, whenever an important event happens to the publisher,
it goes over its subscribers and calls the specific
notification method on their objects.

Real apps might have dozens of different subscriber


classes that are interested in tracking events of the same
publisher class. You wouldn’t want to couple the publisher to
all of those classes. Besides, you might not even know about
some of them beforehand if your publisher class is
supposed to be used by other people.

That’s why it’s crucial that all subscribers implement the


same interface and that the publisher communicates with
them only via that interface. This interface should declare the
notification method along with a set of parameters that the
publisher can use to pass some contextual data along with
the notification.

Publisher notifies subscribers by calling the specific notification method on


their objects.
If your app has several different types of publishers and
you want to make your subscribers compatible with all of
them, you can go even further and make all publishers
follow the same interface. This interface would only need
to describe a few subscription methods. The interface
would allow sub- scribers to observe publishers’ states
without coupling to their concrete classes.

 Real-World Analogy
If you subscribe to a newspaper or magazine, you no
longer need to go to the store to check if the next issue is
available. Instead, the publisher sends new issues directly to
your mail- box right after publication or even in advance.

Magazine and newspaper subscriptions.

The publisher maintains a list of subscribers and knows which


magazines they’re interested in. Subscribers can leave the
list
at any time when they wish to stop the publisher sending new
magazine issues to them.

 Structure

1. The Publisher issues events of interest to other objects.


These events occur when the publisher changes its state or
executes some behaviors. Publishers contain a subscription
infrastruc- ture that lets new subscribers join and current
subscribers leave the list.

2. When a new event happens, the publisher goes over the sub-
scription list and calls the notification method declared in
the subscriber interface on each subscriber object.

3. The Subscriber interface declares the notification interface.


In most cases, it consists of a single update method. The
method may have several parameters that let the publisher
pass some event details along with the update.
4. Concrete Subscribers perform some actions in response to
notifications issued by the publisher. All of these classes must
implement the same interface so the publisher isn’t coupled
to concrete classes.

5. Usually, subscribers need some contextual information to


han- dle the update correctly. For this reason, publishers
often pass some context data as arguments of the notification
method. The publisher can pass itself as an argument,
letting sub- scriber fetch any required data directly.

6. The Client creates publisher and subscriber objects


separately and then registers subscribers for publisher
updates.

 Pseudocode
In this example, the Observer pattern lets the text editor
object notify other service objects about changes in its state.
Notifying objects about events that happen to other objects.

The list of subscribers is compiled dynamically: objects can


start or stop listening to notifications at runtime, depending
on the desired behavior of your app.

In this implementation, the editor class doesn’t maintain the


subscription list by itself. It delegates this job to the
special helper object devoted to just that. You could
upgrade that object to serve as a centralized event
dispatcher, letting any object act as a publisher.
Adding new subscribers to the program doesn’t require
changes to existing publisher classes, as long as they work
with all subscribers through the same interface.

1 // The base publisher class includes subscription management


2 // code and notification methods.
3 class EventManager is
4 private field listeners: hash map of event types and listeners
5
6 method subscribe(eventType, listener) is
7 listeners.add(eventType, listener)
8
9 method unsubscribe(eventType, listener) is
10 listeners.remove(eventType, listener)
11
12 method notify(eventType, data) is
13 foreach (listener in listeners.of(eventType)) do
14 listener.update(data)
15
16 // The concrete publisher contains real business logic that's
17 // interesting for some subscribers. We could derive this class
18 // from the base publisher, but that isn't always possible in
19 // real life because the concrete publisher might already be a
20 // subclass. In this case, you can patch the subscription logic
21 // in with composition, as we did here.
22 class Editor is
23 private field events: EventManager
24 private field file: File
25
26 constructor Editor() is
27 events = new EventManager()
28 // Methods of business logic can notify subscribers about
29 // changes.
30 method openFile(path) is
31 this.file = new File(path)
32 events.notify("open", file.name)
33
34 method saveFile() is
35 file.write()
36 events.notify("save", file.name)
37
38 // ...
39
40
41 // Here's the subscriber interface. If your programming language
42 // supports functional types, you can replace the whole
43 // subscriber hierarchy with a set of functions.
44 interface EventListener is
45 method update(filename)
46
47 // Concrete subscribers react to updates issued by the publisher
48 // they are attached to.
49 class LoggingListener implements EventListener is
50 private field log: File
51 private field message
52
53 constructor LoggingListener(log_filename, message) is
54 this.log = new File(log_filename)
55 this.message = message
56
57 method update(filename) is
58 log.write(replace('%s',filename,message))
59
60 class EmailAlertsListener implements EventListener is
61 private field email: string
62
63 constructor EmailAlertsListener(email, message) is
64 this.email = email
65 this.message = message
66
67 method update(filename) is
68 system.email(email, replace('%s',filename,message))
69
70
71 // An application can configure publishers and subscribers at
72 // runtime.
73 class Application is
74 method config() is
75 editor = new TextEditor()
76
77 logger = new LoggingListener(
78 "/path/to/log.txt",
79 "Someone has opened the file: %s");
80 editor.events.subscribe("open", logger)
81
82 emailAlerts = new EmailAlertsListener(
83 "[email protected]",
84 "Someone has changed the file: %s")
COMPOSITE
Also known as: Object Tree

Composite is a structural design pattern that lets you


compose objects into tree structures and then work with
these structures as if they were individual objects.
 Problem
Using the Composite pattern makes sense only when the core
model of your app can be represented as a tree.

For example, imagine that you have two types of objects:


Products and Boxes . A Box can contain severalProducts
Boxes can
as well as a number of smaller Boxes . These little
also hold some Products or even smaller Boxes , and so on.

An order might comprise various products, packaged in boxes, which are


packaged in bigger boxes and so on. The whole structure looks like an
upside down tree.
Say you decide to create an ordering system that uses
these classes. Orders could contain simple products
without any wrapping, as well as boxes stuffed with
products...and other boxes. How would you determine the
total price of such an order?

You could try the direct approach: unwrap all the boxes,
go over all the products and then calculate the total. That
would be doable in the real world; but in a program, it’s not as
simple as running a loop. You have to know the classes of
Products

and Boxes you’re going through, the nesting level of the


boxes and other nasty details beforehand. All of this makes the
direct approach either too awkward or even impossible.

 Solution
The Composite pattern suggests that you work with Products
and Boxes through a common interface which declares a
method for calculating the total price.

How would this method work? For a product, it’d simply


return the product’s price. For a box, it’d go over each item
the box contains, ask its price and then return a total for
this box. If one of these items were a smaller box, that
box would also start going over its contents and so on,
until the prices of all inner components were calculated. A
box could even add some extra cost to the final price, such as
packaging cost.
The Composite pattern lets you run a behavior recursively over all
components of an object tree.

The greatest benefit of this approach is that you don’t need


to care about the concrete classes of objects that compose
the tree. You don’t need to know whether an object is a
simple product or a sophisticated box. You can treat them all
the same via the common interface. When you call a method,
the objects themselves pass the request down the tree.

 Real-World Analogy

An example of a military structure.


Armies of most countries are structured as hierarchies. An
army consists of several divisions; a division is a set of
brigades, and a brigade consists of platoons, which can be
bro- ken down into squads. Finally, a squad is a small
group of real soldiers. Orders are given at the top of the
hierarchy and passed down onto each level until every
soldier knows what needs to be done.

 Structure
1. The Component interface describes operations that are com-
mon to both simple and complex elements of the tree.

2. The Leaf is a basic element of a tree that doesn’t have sub-ele-


ments.

Usually, leaf components end up doing most of the real work,


since they don’t have anyone to delegate the work to.

3. The Container (aka composite) is an element that has sub-ele-


ments: leaves or other containers. A container doesn’t know
the concrete classes of its children. It works with all sub-ele-
ments only via the component interface.

Upon receiving a request, a container delegates the work to


its sub-elements, processes intermediate results and then
returns the final result to the client.

4. The Client works with all elements through the component


interface. As a result, the client can work in the same way
with both simple or complex elements of the tree.

 Pseudocode
In this example, the Composite pattern lets you implement
stacking of geometric shapes in a graphical editor.
The geometric shapes editor example.

The CompoundGraphic class is a container that can comprise


any number of sub-shapes, including other compound shapes.
A compound shape has the same methods as a simple
shape. However, instead of doing something on its own, a
compound shape passes the request recursively to all its
children and “sums up” the result.

The client code works with all shapes through the single
inter- face common to all shape classes. Thus, the client
doesn’t
know whether it’s working with a simple shape or a com-
pound one. The client can work with very complex object
structures without being coupled to concrete classes that
form that structure.

1 // The component interface declares common operations for both


2 // simple and complex objects of a composition.
3 interface Graphic is
4 method move(x, y)
5 method draw()
6
7 // The leaf class represents end objects of a composition. A
8 // leaf object can't have any sub-objects. Usually, it's leaf
9 // objects that do the actual work, while composite objects only
10 // delegate to their sub-components.
11 class Dot implements Graphic is
12 field x, y
13
14 constructor Dot(x, y) { ... }
15
16 method move(x, y) is
17 this.x += x, this.y += y
18
19 method draw() is
20 // Draw a dot at X and Y.
21
22 // All component classes can extend other components.
23 class Circle extends Dot is
24 field radius
25
26
27 constructor Circle(x, y, radius) { ... }
28
29 method draw() is
30 // Draw a circle at X and Y with radius R.
31
32 // The composite class represents complex components that may
33 // have children. Composite objects usually delegate the actual
34 // work to their children and then "sum up" the result.
35 class CompoundGraphic implements Graphic is
36 field children: array of Graphic
37
38 // A composite object can add or remove other components
39 // (both simple or complex) to or from its child list.
40 method add(child: Graphic) is
41 // Add a child to the array of children.
42
43 method remove(child: Graphic) is
44 // Remove a child from the array of children.
45
46 method move(x, y) is
47 foreach (child in children) do
48 child.move(x, y)
49
50 // A composite executes its primary logic in a particular
51 // way. It traverses recursively through all its children,
52 // collecting and summing up their results. Since the
53 // composite's children pass these calls to their own
54 // children and so forth, the whole object tree is traversed
55 // as a result.
56 method draw() is
57 // 1. For each child component:
58 // - Draw the component.
59 // - Update the bounding rectangle.
60 // 2. Draw a dashed rectangle using the bounding
61 // coordinates.
62
63
64 // The client code works with all the components via their base
65 // interface. This way the client code can support simple leaf
66 // components as well as complex composites.
67 class ImageEditor is
68 method load() is
69 all = new CompoundGraphic()
70 all.add(new Dot(1, 2))
71 all.add(new Circle(5, 3, 10))
72 // ...
73
74 // Combine selected components into one complex composite
75 // component.
76 method groupSelected(components: array of Graphic) is
77 group = new CompoundGraphic()
78 group.add(components)
79 all.remove(components)
80 all.add(group)
81 // All components will be drawn.
82 all.draw()

You might also like