The containing class is said to have a has-a relationship with the contained class.
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Class1 {
}
class Class2 {
}
Class1 "1" o-- "*" Class2
classClass1{List<Class2> c2s;// Class1 has class2s}// ExampleclassDepartment{List<Employee> employees;// Department has employees}
Composition
The containing class is said to have a part-of relationship with the contained class.
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Class1 {
}
class Class2 {
}
Class1 "1" *-- "*" Class2
classClass1{privateClass2 c2 =newClass2();// Class2 cannot exist without Class1}// ExampleclassHouse{privateRoom room =newRoom();// Room cannot exist without House}
Dependency
Class1 depends-on Class2
Exists between two classes if the changes to the definition of one may cause changes to the other (but not the other way around).
Represented bys dashed line with an open arrow
classDiagram
direction LR
class Class1 {
}
class Class2 {
}
Class1 ..> Class2
classClass1{voidmethod(){Class2 c3 =newClass2();// Temporary dependency with Class2 c2.methodFromC2();}}// ExampleclassOrderProcessor{voidprocess(Order order){// association with OrderPaymentGateway gateway =newPaymentGateway();// Temporary dependency with PaymentGateway gateway.charge(order.getAmount());}}
Design Patterns
Behavioral Design Patterns
Memento Pattern
Allows restoring an object to a previous state.
Applicability
Use the Memento pattern when
a snapshot of (some portion of) an object's state must be saved so that it can be restored to that state later, and
a direct interface to obtaining the state would expose implementation details
and break the object's encapsulation.
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Editor {
- content: String
+ createState(): EditorState
+ restore(EditorState): void
+ getContent(): String
+ setContent(String): void
}
class EditorState {
- content: String
+ getContent(): String
}
class History {
- states: Stack
+ push(EditorState): void
+ pop(): EditorState
}
Editor --> EditorState
History o-- EditorState
General Vocabulary
Editor is called Originator
EditorState is called Memento
History is called Caretaker
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Originator {
- state
+ createMemento(): Memento
+ restore(Memento): void
+ getMemento()
+ setMemento(Memento): void
}
class Memento {
- state
+ getState()
}
class Caretaker {
- states: List
+ push(Memento): void
+ pop(): Memento
}
Originator --> Memento
Caretaker o-- Memento
publicclassMain{publicstaticvoidmain(String[] args){var editor =newEditor();var history =newHistory(); editor.setContent("a"); history.push(editor.createState()); editor.setContent("b"); history.push(editor.createState()); editor.setContent("c"); history.push(editor.createState());System.out.println(editor.getContent());// c// Undo operations editor.restore(history.popFromUndo());System.out.println(editor.getContent());// b editor.restore(history.popFromUndo());System.out.println(editor.getContent());// a// Redo operations editor.restore(history.popFromRedo());System.out.println(editor.getContent());// b editor.restore(history.popFromRedo());System.out.println(editor.getContent());// c}}
Observer Pattern
Allows an object notify other objects when its state changes.
Applicability
Use the Observer pattern in any of the following situations:
When a change to one object requires changing others, and you don't know how many objects need to be changed.
When an object should be able to notify other objects without making assumptions about who these objects are. In other words, you don't want these objects tightly coupled.
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class DataSource {
- observers: List
- value: int
+ getValue(): int
+ setValue(int): void
+ addObserver(Observer): void
+ removeObserver(Observer): void
+ notifyObservers(): void
}
class Observer {
<<interface>>
+ update(): void
}
class BarChart {
+ update(): void
}
class PieChart {
+ update(): void
}
DataSource "1" o-- "*" Observer
Observer <|-- BarChart
Observer <|-- PieChart
General Vocabulary
DataSource is called Subject
BarChart / PieChart is called ConcreteObserver
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Subject {
- observers: List
- value: int
+ getValue(): int
+ setValue(int): void
+ addObserver(Observer): void
+ removeObserver(Observer): void
+ notifyObservers(): void
}
class Observer {
<<interface>>
+ update(): void
}
class ConcreteObserver {
+ update(): void
}
Subject "1" o-- "*" Observer
Observer <|-- ConcreteObserver
publicclassPenToolimplementsTool{@OverridepublicvoidmouseDown(){System.out.println("Pen icon");}@OverridepublicvoidmouseUp(){System.out.println("Draw a line");}}
Implement Concrete Tool: Selection Tool
publicclassSelectionToolimplementsTool{@OverridepublicvoidmouseDown(){System.out.println("Selection icon");}@OverridepublicvoidmouseUp(){System.out.println("Draw a dashed rectangle");}}
With direct methods in the aggregate, you can only have ONE iteration at a time.
// BAD: Direct implementation in aggregatevar history =newBrowseHistory();// Only ONE position tracker in the classhistory.next();// moves position to 1history.next();// moves position to 2// Can't have multiple iterations simultaneously
With separate iterators, you can have multiple independent iterations supporting thread safety. (Current way)
// GOOD: Iterator patternvar history =newBrowseHistory();Iterator iterator1 = history.createIterator();Iterator iterator2 = history.createIterator();iterator1.next();// iterator1 at position 1, iterator2 still at 0iterator2.next();// iterator2 at position 1, iterator1 still at 1
Why nested Concrete Iterator?
The inner class doesn't pollute the global namespace.
In a package, You can have many Aggregate class that may need same name ArrayIterator.
Encapsulation Violation as Client code must know the internal implementation details of size and urls
Tight Coupling as Changes to the internal structure (e.g., switching from array to LinkedList) would break all client code
Strategy Pattern
Allows passing different algorithms (behaviors) to an object.
Makes algorithm interchangeable.
Allows defining a template (skeleton) for an operation. Specific steps will then
be implemented in subclasses.
Applicability
Use the Strategy pattern in any of the following situations:
You need different variants of an algorithm. For example, Photoshop requires many compression algorithm depending type of image and image compression ratio requirement of user. Also may require different filters like black & white filter, high contrast filter, etc.
Algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.
A class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class.
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class PaymentService {
- strategy: PaymentStrategy
+ setPaymentStrategy(PaymentStrategy): void
+ pay(): void
}
class PaymentStrategy {
<<interface>>
+ processPayment(): void
}
class DebitCardPayment {
+ processPayment(): void
}
class EsewaPayment {
+ processPayment(): void
}
PaymentService o-- PaymentStrategy
PaymentStrategy <|-- DebitCardPayment
PaymentStrategy <|-- EsewaPayment
General Vocabulary
PaymentService is called Context
PaymentStrategy is called Strategy
DebitCardPayment / EsewaPayment is called ConcreteStrategy
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Context {
- strategy: Strategy
+ setStrategy(Strategy): void
+ process(): void
}
class Strategy {
<<interface>>
+ process(): void
}
class ConcreteStrategy {
+ process(): void
}
Context o-- Strategy
Strategy <|-- ConcreteStrategy
publicclassPaymentService{publicvoidprocessPayment(String paymentMethod){if(paymentMethod.equals("Credit Card")){System.out.println("Making payment via credit card");}elseif(paymentMethod.equals("Debit Card")){System.out.println("Making payment via debit card");}elseif(paymentMethod.equals("Esewa")){//huge algorithmSystem.out.println("Making payment via Esewa");}else{System.out.println("Unsupported Payment method");}}}publicclassMain{publicstaticvoidmain(String[] args){PaymentService paymentService =newPaymentService(); paymentService.processPayment("Esewa");}}
Command Pattern
Allows decouple a sender from a receiver. The sender will talk to the receive through a command.
Commands can be undone and persisted.
Encapsulate a request as an object, thereby letting you parameterize clients with
different requests, queue or log requests, and support undoable operations.
Applicability
Use the Command pattern when
Callback function is a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks
Specify, queue, and execute requests at different times.
Support undo. TheCommand's Execute operation can store state for reversing its effects in the command itself
Support logging changes so that they can be reapplied in case of a system crash.
Violates Dependency Inversion Principle (DIP). The Button class (high-level module) now directly depends on CustomerService (low-level module).
Violates Open/Closed Principle (OCP). The Button class is not open for extension but requires modification to support different operations. To add a "Delete Customer" button, you'd need to modify the Button class or create a new button class.
Violates Loose Coupling. The Button is now tightly coupled to CustomerService. Any changes to CustomerService might affect Button, and Button can only work with CustomerService operations
// Direct use on clickpublicclassButton{privateTextEditor editor;publicButton(TextEditor editor){this.editor = editor;}publicvoidclick(){ editor.boldText();}}// SamepublicclassTextEditor{publicvoidboldText(){System.out.println("Text has been bolded.");}}// Main class - without Command patternpublicclassMain{publicstaticvoidmain(String[] args){TextEditor textEditor =newTextEditor();Button button =newButton(textEditor); button.click();}}
Composite Command
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Command {
<<interface>>
+ execute()
}
class ConcreteCommand {
- service
+ execute()
}
class CompositeCommand {
- commands: List
+ addCommand(Command)
+ execute()
}
Command <|-- ConcreteCommand
Command <|-- CompositeCommand
ConcreteCommand "*" --o "1" CompositeCommand
Add italicizeText functionality in CustomerService
publicvoiditalicizeText(){System.out.println("Text has been italicized.");}
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Command {
<<interface>>
+ execute()
}
class UndoableCommand {
<<interface>>
+ unexecute()
}
class ConcreteUndoableCommand {
+ execute()
+ unexecute()
}
class ConcreteCommand {
+ execute()
}
Command <|-- UndoableCommand: extends
UndoableCommand <|-- ConcreteUndoableCommand: implements
Command <|-- ConcreteCommand: implements
History
Memento pattern can be used, however we use snapshot of data in memento pattern
Storing snapshot of data might not be necessary, we only save commands
For eg: if functionality is to change video from 1080p to 720p, it is not sensible to save snapshot of entire video. we only save command of 1080p to 720p. So, Memento class is not created.
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class History {
- undoableCommands: Stack
+ push(UndoableCommand)
+ pop(): UndoableCommand
}
class TextEditor {
- content: String
+ boldText()
+ setContent(String)
+ getContent(): String
}
class BoldCommand {
- prevContent: String
- editor: TextEditor
- history: History
+ execute()
+ unexecute()
}
class UndoCommand {
- history: History
+ execute()
}
UndoCommand o-- History
BoldCommand o-- TextEditor
BoldCommand o-- History
Complete Command Pattern with History
%%{init: { "flowchart": { "rankSpacing": 100, "nodeSpacing": 100 }}}%%
classDiagram
direction LR
class Command {
<<interface>>
+ execute()
}
class UndoableCommand {
<<interface>>
+ unexecute()
}
class Button {
- command: Command
+ click(): void
}
class History {
- undoableCommands: Stack
+ push(UndoableCommand)
+ pop(): UndoableCommand
}
class TextEditor {
- content: String
+ boldText()
+ setContent(String)
+ getContent(): String
}
class BoldCommand {
- prevContent: String
- editor: TextEditor
- history: History
+ execute()
+ unexecute()
}
class UndoCommand {
- history: History
+ execute()
}
Command <|-- UndoableCommand
UndoableCommand <|-- BoldCommand
Command <|-- UndoCommand
History o-- "*" UndoableCommand
UndoCommand o-- History
BoldCommand o-- TextEditor
BoldCommand o-- History
Button o-- Command