Download as pdf or txt
Download as pdf or txt
You are on page 1of 69

VIETNAM NATIONAL UNIVERSITY, HO CHI MINH CITY UNIVERSITY OF

TECHNOLOGY
FACULTY OF COMPUTER SCIENCE AND ENGINEERING

ADVANCED PROGRAMMING (CO2039)

Assignment
Design Patterns in Object Oriented
Programming & New Programming
Approaches

Advisor: Truong Tuan Anh


HO CHI MINH CITY, Jun 2023
Section I.01 Design patterns in Object Oriented Programming

(a) What are design patterns?

In software engineering and especially object-oriented programming, design pattern is a general


blueprint that can be used to tackle commonly occurring problems in programming. It is neither a
programming language or a direct solution to any problem. It is a description or template for how
to solve a problem that can be used in many different situations and many programming
languages. Furthermore, design patterns are not initially created as an invention, but rather
solutions to general problems that software developers faced during initial development, which
were obtained trials after trials by numerous software developers over quite a substantial period
of time.

For example, in real life, a text edit application programmer realized that many times, users
would make mistakes and want to come back to the state where they haven’t committed that
error. That is why the “undo” button was created in the first case. In this example, the repeating
problem is the mistakes that users usually make and an elegant general solution to that problem
is to provide a way for users to come back to their previous states – the undo button.

(b) Three design patterns category:

● Creational patterns

These design patterns provide a way to initialize an object without revealing the logic behind it.
This makes our program more flexible in determining which objects need to be created for a
given use case. This pattern can be further divided into class-creation patterns and
object-creational patterns. While the class-creation patterns use inheritance between classes
effectively in the instantiation process, object-creation patterns use delegation effectively to get
the job done.

There are 5 specific creational design patterns:

1. Factory method

Definition:
Factory method is a creational design pattern that defines an interface for creating an object, but
let subclasses decide which class to call constructor function. Factory Method lets a class defer
instantiation to its sub – classes. As the name suggest, the factory method concentrates mainly on
the method to create 1 object of a class, only a method is used in this case.

Goal:
The factory pattern aims to solve a fundamental problem in instantiation – i.e., the creation of a
concrete object of a class – in object-oriented programming. In principle, creating an object
directly within the class that needs or should use this object is possible, but very inflexible. It
binds the class to this object and makes it impossible to change the instantiation independently of
the class.

Advantages:
o Modular expandability of the application
o Great for testing
o Significant method names

Disadvantages:
o Number of classes can be redundant
o Application extension is complex

Structure:

1. factory method structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
class Pizza {
public:
virtual ~Pizza() {}
virtual std::string Operation() const = 0;
};
class chickenPizza : public Pizza {
public:
std::string Operation() const override {
return "a chicken Pizza was made";
}
};
class pepperPizza : public Pizza {
public:
std::string Operation() const override {
return "a peperroni Pizza was made";
}
};
class Maker {
public:
virtual ~Maker(){};
virtual Pizza* FactoryMethod() const = 0;
std::string SomeOperation() const {
Pizza* Pizza = this->FactoryMethod();
std::string result = "The chefs are doing their job " + Pizza->Operation();
delete Pizza;
return result;
}
};
class Chef1 : public Maker {
public:
Pizza* FactoryMethod() const override {
return new chickenPizza();
}
};
class Chef2 : public Maker {
public:
Pizza* FactoryMethod() const override {
return new pepperPizza();
}
};

void Customer(const Maker& Maker) {


std::cout << "Customer doesn't need to know which chef is cooking\n"<< Maker.SomeOperation() << std::endl;
}
int main() {
std::cout << "Client: Calling first chef.\n";Maker* maker1 = new Chef1();Customer(*maker1);std::cout <<
std::endl;std::cout << "Client: Calling second chef.\n";Maker* makerr = new Chef2();Customer(*makerr);delete
maker1;delete makerr;return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ tempCodeRunnerFile.cpp -o temp
CodeRunnerFile && "/Users/khuenguyen/Desktop/haskell/"tempCodeRu
nnerFile
Client: Calling first chef.
Customer doesn't need to know which chef is cooking
The chefs are doing their job a chicken Pizza was made

Client: Calling second chef.


Customer doesn't need to know which chef is cooking
The chefs are doing their job a peperroni Pizza was made

UML diagram:
2. Abstract factory

Definition:
By initializing the "factory" that creates certain types of related products, you can create families
of related products without specifying their concrete classes thanks to the creational design
pattern known as Abstract Factory.

Goal:
You can create objects from each class of the product family using the interface provided by the
Abstract Factory. You don't have to be concerned about creating the incorrect variation of a
product that doesn't match the products already created by your app as long as your code
produces objects via this interface. It produces a well-designed program in which each class is in
charge of just one task.

Advantages:
o Ensure that the products got from a “factory” are compatible with each other.
o Avoid tight coupling between concrete products and client code.
o Single Responsibility Principle: can extract the product creation code into one place,
making the code more supportable.
o Open/Closed Principle: can introduce new variants of products without modifying
existing client code.

Disadvantages:
o The code may become more complex than it should be, since a lot of new interfaces
and classes are introduced along with the pattern.

Structure:
2. abstract factory structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
using namespace std;
class Phone{
public:
virtual void showInfo() const =0;
};
class Iphone : public Phone{
public:
void showInfo() const override{
cout<<"Phone's brand: Apple --- version Iphone 14 Promax\n";
}
};
class SamsungPhone : public Phone{
public:
void showInfo() const override{
cout<<"Phone's brand: Samsung --- version Samsung Galaxy Z-flip\n";
}
};
class tab{
public:
virtual void showInfo() const =0;
};
class Ipad : public tab{
public:
void showInfo() const override{
cout<<"Tablet's brand: Apple --- version Ipad Air gen 10th\n";
}
};
class Tab : public tab{
public:
void showInfo() const override{
cout<<"Tablet's brand: Samsung --- version Samsung Galaxy Tab E7\n";
}
};
class AbstractFactory{
public:
virtual Phone* createPhone() const = 0;
virtual tab* createTablet() const = 0;
};
class Apple : public AbstractFactory{
Phone* createPhone() const override{
return new Iphone;
}
tab* createTablet() const override{
return new Ipad;
}
};
class Samsung : public AbstractFactory{
Phone* createPhone() const override{
return new SamsungPhone;
}
tab* createTablet() const override{
return new Tab;
}
};
void manufacture(const AbstractFactory& f){
cout<<"....Manufacturing....\n";
const Phone* a= f.createPhone();
const tab* b= f.createTablet();
a->showInfo();
b->showInfo();
}
int main(){
cout<<"Apple";
Apple* f1 = new Apple();
manufacture(*f1);
delete f1;
cout<<endl<<"Samsung";
Samsung* f2 = new Samsung();
manufacture(*f2);
delete f2;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ tempCodeRunnerFile.cpp -o temp
CodeRunnerFile && "/Users/khuenguyen/Desktop/haskell/"tempCodeRu
nnerFile
Apple....Manufacturing....
Phone's brand: Apple --- version Iphone 14 Promax
Tablet's brand: Apple --- version Ipad Air gen 10th

Samsung....Manufacturing....
Phone's brand: Samsung --- version Samsung Galaxy Z-flip
Tablet's brand: Samsung --- version Samsung Galaxy Tab E7

UML diagram:
3. Builder

Definition:
Builder is a creational design pattern that lets you initialize complex objects elaborately. The
pattern allows you to produce any type of the object you like using the same construction code
iteratively.

Goal:
Using just the steps you really need, you are able to construct objects using the Builder pattern.
You no longer need to stuff dozens of arguments into your constructors once you've implemented
the pattern. The director class controls the construction schedule in the interim.

Advantages:
o Can construct objects step-by-step, defer construction steps or run steps recursively.
o Can reuse the same construction code to build various type of the same product.
o Single Responsibility Principle: can separate complex construction code from the
business logic of the product.

Disadvantages:
o The overall complexity of the code increases because the pattern requires creating
multiple new classes.

Structure:
3. builder structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <iostream>
#include <string>

using namespace std;


class Math{
bool isEquation;
string eqt;
int id;
public:
Math(int idd,bool is){
this->id=idd;
this->isEquation=is;
}
static Math create(int id){ return Math{id,false};}
void show()const{
cout<<"Equation id: "<<id<<endl;
cout<<"\""<<eqt<<"\""<<endl;
}
Math& addInt(int a){
eqt+=to_string(a);
return *this;
}
Math& addOp(char a){
eqt+=a;
return *this;
}
Math& isEQ(){
this->isEquation=true;
return *this;
}
};
int main(){
bool eq=false;
const auto newEquation = Math::create(12).addInt(2).addOp('x').addInt(3);
newEquation.show();
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ tempCodeRunnerFile.cpp -o temp
CodeRunnerFile && "/Users/khuenguyen/Desktop/haskell/"tempCodeRu
nnerFile
Equation id: 12
"2x3"

UML diagram:

4. Prototype

Definition:
Prototype is a creational design pattern that lets you duplicate existing objects independently on
their classes.

Goal:
A group of pre-built objects that have been configured in different ways may also be used as
prototypes thanks to the Prototype pattern. The client can simply choose a suitable prototype and
clone it rather than creating a subclass that suits a particular configuration.

Advantages:
o Can clone existing objects without depending their concrete classes.
o Can get rid of repeated initialization code in favor of cloning pre-built prototypes.
o Can reproduce complex objects easily.
o You get an alternative to inheritance when dealing with configuration presets for
complex objects.

Disadvantages:
o Cloning complex objects that have circular references might be very complex.

Structure:

4. prototype structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
#include<string>
using namespace std;
class Allien {
protected:
string name;
public:
Allien() {}
Allien(string Allien_name)
: name(Allien_name) {
}
virtual ~Allien() {}
virtual Allien *Clone() const = 0;
virtual void Method() {
std::cout << "An allien was created, type: " <<name<< std::endl;
}
};
class Allien1 : public Allien {
public:
Allien1(string Allien_name)
: Allien(Allien_name) {
}
Allien1 *Clone() const override {
return new Allien1(*this);
}
};

class Allien2 : public Allien {


private:
public:
Allien2(string Allien_name)
: Allien(Allien_name) {
}
Allien2 *Clone() const override {
return new Allien2(*this);
}
};
int main() {
cout<<"...Creating Predator...\n";
Allien1 temp("Predator");
temp.Method();
cout<<"...Cloning allien...\n";
Allien1 * clone = temp.Clone();
clone->Method();
return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
...Creating Predator...
An allien was created, type: Predator
...Cloning allien...
An allien was created, type: Predator

UML diagram:
5. Singleton

Definition:
Singleton is a creational design pattern ensuring that an object only has 1 instance of its own,
while providing global access to it.

Goal:
Make sure each instance of a class is unique. The Singleton pattern only allows the specific
creation method to be used to create objects of a class. If an object has already been created, this
function either returns it or creates a new one if necessary. Additionally, you must provide that
instance a global access point.

Advantages:
o Ensures that a class has only 1 instance.
o Gain global access point to that instance.
o The singleton object is initialized only at first time reference.

Disadvantages:
o Violates the Single Responsibility Principle. The pattern solves two problems at the
time. (1 is ensure a class has only 1 instance, 2 is provide global access to that
variable).
o The Singleton pattern can mask bad design, for instance, when the components of the
program know too much about each other.
o Synchronization can be a problem.
o Hard for debugging
Because of the many problems that the Singleton pattern brings, it is usually considered a bad
design pattern. But this is a totally incorrect assumption as some problems could be solved easily
using this design pattern without actually causing any consequences while others just couldn’t.

Structure:

5. singleton structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <string>
#include <iostream>
using namespace std;
class Database{
static Database* singleton;
string name;
Database(string name)
{
this->name = name;
}
public:
string getName()
{
return name;
}
static Database* getInstance(string name)
{
if (nullptr == singleton) {
singleton = new Database(name);
return singleton;
}
else{
singleton->name = name;
return singleton;
}
}
};
Database* Database::singleton = nullptr;
int main()
{
Database *database1;
database1 = Database::getInstance("Health");
cout<<"First database created, specialty: "<<database1->getName()<<endl;
Database * database2 = Database::getInstance("Insurance");
cout<<"Second database created, specialty: "<<database2->getName()<<endl;
cout<<"Now the first data base created becomes: "<<database1->getName();
return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ tempCodeRunnerFile.cpp -o temp
CodeRunnerFile && "/Users/khuenguyen/Desktop/haskell/"tempCodeRu
nnerFile
First database created, specialty: Health
Second database created, specialty: Insurance
Now the first data base created becomes: Insurance

UML diagram:

● Structural patterns

In order to make bigger structures flexible and effective, structural design patterns describe how
to combine objects and classes. The patterns in structural designs demonstrate how different
system components may be coupled in an adaptable and extendable way to create a larger
structure for attaining many goals at once or modifying particular portions of the structure
without changing the entire structure.

6. Adapter

Definition:
Adapter is a structural design pattern that allows objects with incompatible interfaces to
cooperate and interact through an Adapter object.

Goals:
Create a middle-layer class that serves as a translator between your code and a legacy class, a
3rd-party class or any other class with an unknown interface.

Advantages:
o Single Responsibility Principle: can separate the interface or data conversion code
from the main business logic of the program.
o Open/Closed Principle: can introduce new types of adapters into the program without
breaking existing client code.

Disadvantages:
o The overall complexity of the code increases because the need to introduce a set of
new interfaces and classes.

Structure:

6. adapter structure (https://refactoring.guru/design-patterns)

Code example:
Source:
#include<iostream>
#include<string>
using namespace std;
class Normal{
public:
virtual string Op() const {
return "\"This is what a normal text looks like\"";
}
};
class Reversed{
public:
string action() const{
return "\"!desrever neeb sah txet sihT\"";
}
};
class Adapter : public Normal{
Reversed* reversed;
public:
Adapter(Reversed* r) : reversed(r){}

string Op() const override{


string temp = reversed->action();
reverse(temp.begin(),temp.end());
return "Translated by Adapter: " + temp;
}
};
int main(){
cout<<"--I can only understand normal text--\n";
Normal* n = new Normal;
cout<<n->Op()<<endl;
cout<<"--Allien can only speak reversed text--\n";
Reversed* text = new Reversed;
cout<<text->action()<<endl;
cout<<"--I cannot understand what allien text looks like, but i can use an Adapter to translate!--\n";
Adapter* a = new Adapter(text);
cout<<a->Op()<<endl;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ tempCodeRunnerFile.cpp -o temp
CodeRunnerFile && "/Users/khuenguyen/Desktop/haskell/"tempCodeRu
nnerFile
--I can only understand normal text--
"This is what a normal text looks like"
--Allien can only speak reversed text--
"!desrever neeb sah txet sihT"
--I cannot understand what allien text looks like, but i can use an Adapter to translate!--
Translated by Adapter: "This text has been reversed!"

UML diagram:
7. Decorator

Definition:
With the use of a structural design pattern called Decorator, you may give existing objects new
behaviors by enclosing them in special “wrapper” objects that possesses new attributes but also
contain the old behaviors from the previous object.

Goals:
The decorator enables you to layer your business logic, create a decorator for each layer, and
construct objects at runtime using different combinations of this logic. Since each of these
objects adheres to a standard interface, the client programs may handle them all uniformly.

Advantages:
o Can extend an object’s behavior without making a new subclass.
o Can add or remove responsibilities from an object at runtime.
o Can combine several behaviors by wrapping an object into multiple decorators.
o Single Responsibility Principle: can divide a monolithic class that implements many
possible variants of behavior into several smaller classes.

Disadvantages:
o It’s hard to remove a specific wrapper from the wrappers stack.
o It’s hard to implement a decorator in such a way that its behavior doesn’t depend on
the order in the decorators’ stack.
o The initial configuration code of layers might look pretty ugly.

Structure:
7. decorator structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <iostream>
#include<string>
using namespace std;
class Comp{
public:
virtual void Show() const = 0;
};
class Inside : public Comp{
public:
void Show() const override{cout<<"This is what stored inside";}
};
class Deco : public Comp{
protected:
Comp* component;
public:
Deco(Comp* c) : component(c){}
void Show() const override{
this->component->Show();
}
};
class SquareDeco : public Deco{
public:
SquareDeco(Comp* c) : Deco(c){}
void Show() const override{
cout<<"[ ";
Deco::Show();
cout<<" ]";
}
};
class PointDeco : public Deco{
public:
PointDeco(Comp* c) : Deco(c){}
void Show() const override{
cout<<"{ ";
Deco::Show();
cout<<" }";
}
};
int main(){
Comp* temp = new Inside;
cout<<"------This is a simple example------\n";
temp->Show();
cout<<endl;
Comp* deco1 = new SquareDeco(temp);
Comp* deco2 = new PointDeco(deco1);
cout<<"------This is example after decorating------\n";
deco2->Show();

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ memento.cpp -o memento && "/Us
ers/khuenguyen/Desktop/haskell/"memento
------This is a simple example------
This is what stored inside
------This is example after decorating------
{ [ This is what stored inside ] }

UML diagram:

8. Composite:

Definition:
With the use of the structural design pattern known as Composite, you may group things into tree
structures and then treat those structures as individual objects.

Goal:
Simple leaves and complicated containers are two fundamental element kinds that are provided
to you by the Composite pattern. Both leaves and other containers can be used to make a
container. This enables you to create an object structure that is hierarchical and recursive and
looks like a tree. The client doesn't have to worry about the concrete class of the objects it works
with since all of the pieces described by the Composite pattern have a common interface.

Advantages:
o Can work with complex tree structures more conveniently: use polymorphism and
recursion to your advantage.
o Open/Closed Principle: can introduce new element types into the app without
breaking the existing code, which now works with the object tree.

Disadvantages:
o It might be difficult to provide a common interface for classes whose functionality
differs too much. In certain scenarios, you’d need to overgeneralize the component
interface, making it harder to comprehend

Structure:

8. composite structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Graphic {
public:
virtual void print() const = 0;
virtual ~Graphic() {}
};
class GraphicComposite : public Graphic {
std::vector<const Graphic*> children;
const std::string& name;
public:
explicit GraphicComposite(const std::string& n): name(n){}
void print() const override {
std::cout << name << " ";
for (auto c: children) c->print();
}
void add(const Graphic* component) {
children.push_back(component);
}
void erase(vector<const Graphic*> list, const Graphic* component){
for (auto it = list.begin(); it != list.end();
++it){
if(*it==component) children.erase(it);
}
}
void remove(const Graphic* component) {
erase(children, component);
}
};
class Ellipse: public Graphic {
private:
const std::string& name;
public:
explicit Ellipse(const std::string& n): name (n) {}
void print() const override {
std::cout << name << " ";
}
};
int main(){
std::cout << '\n';
const std::string el1 = "ellipse1";const std::string el2 = "ellipse2";const std::string el3 = "ellipse3";const std::string
el4 = "ellipse4";
Ellipse ellipse1(el1);Ellipse ellipse2(el2);Ellipse ellipse3(el3);Ellipse ellipse4(el4);
const std::string graph1 = "graphic1";const std::string graph2 = "graphic2";const std::string graph3 = "graphic3";
GraphicComposite graphic1(graph1);
graphic1.add(&ellipse1);graphic1.add(&ellipse2);graphic1.add(&ellipse3);graphic1.print();
std::cout << '\n';
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final

graphic1 ellipse1 ellipse2 ellipse3

UML diagram:
9. Bridge

Definition:
Bridge is a structural design pattern that enables you to divide a large class or group of related
classes into two distinct hierarchies—abstraction and implementation—that may be developed
separately from each another.

Goal:
Split the monolithic class into several class hierarchies. After this, you can change the classes in
each hierarchy independently of the classes in the others. This approach simplifies code
maintenance and minimizes the risk of breaking existing code.

Advantages:
o Can create platform-independent classes and apps.
o The client code works with high-level abstractions. It isn’t exposed to the platform
details.
o Open/Closed Principle: can introduce new abstractions and implementations
independently from each other.
o Single Responsibility Principle: can focus on high-level logic in the abstraction and
on platform details in the implementation.

Disadvantages:
o You might make the code more complicated by applying the pattern to highly
cohesive classes.

Structure:
9. bridge structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <iostream>
class Implement {
public:
virtual void implementation() const = 0;

virtual ~Implement() = default;


};
class ImplementA: public Implement {
public:
ImplementA() = default;

void implementation() const {


std::cout << "Implementation from A: 112233" << '\n';
}
};
class ImplementB: public Implement {
public:
ImplementB() = default;

void implementation() const {


std::cout << "Implementation from B: 445566" << '\n';
}
};
class Abstract {
public:
virtual void function() const = 0;
virtual ~Abstract() = default;
};
class RefinedAbstract: public Abstract {
public:
RefinedAbstract(Implement& i) :
Implement(i) {
}
void function() const {
std::cout << "Refined Abstraction::function\n";
Implement.implementation();
}
private:
Implement& Implement;
};
int main() {
ImplementA ImplementA;ImplementB ImplementB;

RefinedAbstract refinedAbstract1(ImplementA);RefinedAbstract refinedAbstract2(ImplementB);

Abstract *Abstract1 = &refinedAbstract1;Abstract *Abstract2 = &refinedAbstract2;

Abstract1->function();std::cout << '\n';Abstract2->function();


}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ tempCodeRunnerFile.cpp -o temp
CodeRunnerFile && "/Users/khuenguyen/Desktop/haskell/"tempCodeRu
nnerFile
Refined Abstraction::function
Implementation from A: 112233

Refined Abstraction::function
Implementation from B: 445566

UML diagram:

10. Façade
Definition:
Facade is a structural design pattern that provides a simplified interface to a library, a framework,
or any other complex set of classes.

Goal:
Providing a shortcut to the most-used features of the subsystem which fit most client
requirements. Create facades to define entry points to each level of a subsystem.

Advantages:
o You can isolate your code from the complexity of a subsystem (by requiring them to
communicate only through a façade).

Disadvantages:
o A facade can become a god object coupled to all classes of an app.

Structure:

10. facade structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<string>
#include<iostream>
class Subsystem1 {
public:
std::string Operation1() const {
return "Subsystem1: Ready!\n";
}
// ...
std::string OperationN() const {
return "Subsystem1: Go!\n";
}
};
class Subsystem2 {
public:
std::string Operation1() const {
return "Subsystem2: Get ready!\n";
}
// ...
std::string OperationZ() const {
return "Subsystem2: Fire!\n";
}
};
class Facade {
protected:
Subsystem1 *subsystem1_;
Subsystem2 *subsystem2_;
public:
Facade(
Subsystem1 *subsystem1 = nullptr,
Subsystem2 *subsystem2 = nullptr) {
this->subsystem1_ = subsystem1 ?: new Subsystem1;
this->subsystem2_ = subsystem2 ?: new Subsystem2;
}
~Facade() {
delete subsystem1_;
delete subsystem2_;
}
std::string Operation() {
std::string result = "Facade initializes subsystems:\n";
result += this->subsystem1_->Operation1();
result += this->subsystem2_->Operation1();
result += "Facade orders subsystems to perform the action:\n";
result += this->subsystem1_->OperationN();
result += this->subsystem2_->OperationZ();
return result;
}
};
void ClientCode(Facade *facade) {
// ...
std::cout << facade->Operation();
// ...
}
int main() {
Subsystem1 *subsystem1 = new Subsystem1;
Subsystem2 *subsystem2 = new Subsystem2;
Facade *facade = new Facade(subsystem1, subsystem2);
ClientCode(facade);
delete facade;
return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
Facade initializes subsystems:
Subsystem1: Ready!
Subsystem2: Get ready!
Facade orders subsystems to perform the action:
Subsystem1: Go!
Subsystem2: Fire!

UML diagram:

11. Flyweight

Definition:
A structural design technique called flyweight allows you to fit more objects into the RAM that
is available by sharing common state between several objects rather than storing all of the data in
each one.

Goal:
Mainly used to reduce the number of objects created and to decrease memory use in order to
increase performance.

Advantages:
o You can save lots of RAM, assuming the huge number of flyweights in your program.

Disadvantages:
o You might be trading RAM over CPU cycles when some of the context data needs to
be recalculated each time somebody calls a flyweight method.
o The code becomes much more complicated. New team members will always be
wondering why the state of an entity was separated in such a way.

Structure:
11. flyweight structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
using namespace std;
class Flyweight
{
public:
Flyweight(int value_one)
{
val = value_one;
cout << "A new fly weight is created: " << val << '\n';
}
~Flyweight()
{
cout << val << ' ';
}
void report(int turn)
{
cout << val << turn << ' ';
}
private:
int val;
};
class Factory
{
public:
static Flyweight *get_flyweight(int in)
{
if (!s_pool[in])
s_pool[in] = new Flyweight(in);
return s_pool[in];
}
static void clean_up()
{
cout << "Deleting flyweight value: ";
for (int i = 0; i < row; ++i)
if (s_pool[i])
delete s_pool[i];
cout << '\n';
}
static int row, col;
private:
static Flyweight *s_pool[];
};
int Factory::row= 6, Factory::col = 10;
Flyweight *Factory::s_pool[] =
{
0, 0, 0, 0, 0, 0
};
int main()
{
for (int i = 0; i < Factory::row; ++i)
{
for (int j = 0; j < Factory::col; ++j)
Factory::get_flyweight(i)->report(j);
cout << '\n';
}
Factory::clean_up();
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
A new fly weight is created: 0
00 01 02 03 04 05 06 07 08 09
A new fly weight is created: 1
10 11 12 13 14 15 16 17 18 19
A new fly weight is created: 2
20 21 22 23 24 25 26 27 28 29
A new fly weight is created: 3
30 31 32 33 34 35 36 37 38 39
A new fly weight is created: 4
40 41 42 43 44 45 46 47 48 49
A new fly weight is created: 5
50 51 52 53 54 55 56 57 58 59
Deleting flyweight value: 0 1 2 3 4 5

UML diagram:
12. Proxy

Definition:
A structural design pattern called proxy enables you to create a stand-in or replacement for
another item. Using a proxy, you may conduct an action either before or after the request reaches
the original object, controlling access to it.

Goal:
Make a class of proxy objects that may be utilized in an application instead of the actual objects.
The proxy functions as a layer of middleware between the client and the actual object, and may
thus regulate access to the actual object.

Advantages:
o You can control the service object without clients knowing about it.
o You can manage the lifecycle of the service object when clients don’t care about it.
o The proxy works even if the service object isn’t ready or is not available.
o Open/Closed Principle. You can introduce new proxies without changing the service
or clients’ code.

Disadvantages:
o The code may become more complicated since you need to introduce a lot of new
classes.
o The response from the service might get delayed.

Structure:
12. proxy structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class secretBox{
public:
virtual void require() const =0;
};
class realBox : public secretBox{
public:
void require() const override{
cout<<"The real truth is going to be revealed!!!\n";
}
};
class proxy : public secretBox{
private:
realBox* real;
bool checkk() const{
string input;
cout<<"Please enter password: ";
cin>>input;
if(input=="password123") return true;
else return false;
}
public:
proxy(realBox* r) : real(r){
}
~proxy(){
delete real;
}
void require() const override{
if(this->checkk()){
real->require();
}
else{
cout<<"Fail to require the secret Box!\n";
}
}
};
int main(){
cout<<"Client tries to use the real secret Box\n";
realBox* box = new realBox;
box->require();
cout<<"---------------\nClient tries to access to same secret Box with proxy\n";
proxy* p = new proxy(box);
p->require();
return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ tempCodeRunnerFile.cpp -o temp
CodeRunnerFile && "/Users/khuenguyen/Desktop/haskell/"tempCodeRu
nnerFile
Client tries to use the real secret Box
The real truth is going to be revealed!!!
---------------
Client tries to access to same secret Box with proxy
Please enter password: password123
The real truth is going to be revealed!!!

UML diagram:

● Behaviorial patterns
In software engineering, behavioral design patterns are design patterns that identify common
communication patterns among objects. By doing so, these patterns increase flexibility in
carrying out communication.

13. Chain of responsibility

Definition:
Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of
receivers. Upon receiving a request, each receiver decides whether to process the request or to
pass it to the next receiver in the chain.

Goal:
Chain of responsibility pattern is used to achieve loose coupling in software design where a
request from client is passed to a chain of objects to process them

Advantages:
o You can control the order of request handling.
o Single Responsibility Principle. You can decouple classes that invoke operations from
classes that perform operations.
o Open/Closed Principle. You can introduce new handlers into the app without breaking
the existing client code.

Disadvantages:
o Some requests may end up unhandled.

Structure:

13. chain of responsibility structure (https://refactoring.guru/design-patterns)


Code example:

Source:
#include<iostream>
#include<vector>
class Handler {
public:
virtual Handler *SetNext(Handler *handler) = 0;
virtual std::string Handle(std::string request) = 0;
};
class AbstractHandler : public Handler {
private:
Handler *next_handler_;

public:
AbstractHandler() : next_handler_(nullptr) {
}
Handler *SetNext(Handler *handler) override {
this->next_handler_ = handler;
return handler;
}
std::string Handle(std::string request) override {
if (this->next_handler_) {
return this->next_handler_->Handle(request);
}

return {};
}
};
class MonkeyHandler : public AbstractHandler {
public:
std::string Handle(std::string request) override {
if (request == "Banana") {
return "Monkey: I'll eat the " + request + ".\n";
} else {
return AbstractHandler::Handle(request);
}
}
};
class SquirrelHandler : public AbstractHandler {
public:
std::string Handle(std::string request) override {
if (request == "Nut") {
return "Squirrel: I'll eat the " + request + ".\n";
} else {
return AbstractHandler::Handle(request);
}
}
};
class DogHandler : public AbstractHandler {
public:
std::string Handle(std::string request) override {
if (request == "MeatBall") {
return "Dog: I'll eat the " + request + ".\n";
} else {
return AbstractHandler::Handle(request);
}
}
};
void ClientCode(Handler &handler) {
std::vector<std::string> food = {"Nut", "Banana", "Cup of coffee"};
for (const std::string &f : food) {
std::cout << "Client: Who wants a " << f << "?\n";
const std::string result = handler.Handle(f);
if (!result.empty()) {
std::cout << " " << result;
} else {
std::cout << " " << f << " was left untouched.\n";
}
}
}
int main() {
MonkeyHandler *monkey = new MonkeyHandler;
SquirrelHandler *squirrel = new SquirrelHandler;
DogHandler *dog = new DogHandler;
monkey->SetNext(squirrel)->SetNext(dog);
std::cout << "Chain: Monkey > Squirrel > Dog\n\n";
ClientCode(*monkey);
std::cout << "\n";
std::cout << "Subchain: Squirrel > Dog\n\n";
ClientCode(*squirrel);

delete monkey;
delete squirrel;
delete dog;

return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
Chain: Monkey > Squirrel > Dog

Client: Who wants a Nut?


Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Monkey: I'll eat the Banana.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.

Subchain: Squirrel > Dog

Client: Who wants a Nut?


Squirrel: I'll eat the Nut.
Client: Who wants a Banana?
Banana was left untouched.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.

UML diagram:
14. Command

Definition:
Command is a behavioral design pattern that turns a request into a stand-alone object that
contains all information about the request. This transformation lets you pass requests as a method
argument, delay or queue a request’s execution, and support undoable operations.

Goal:
Include all the data that is required in an object to carry out an action or start an event later. The
name of the method, the object that owns the method, and the values for the method arguments
are all included in this data.

Advantages:
o Single Responsibility Principle: You can decouple classes that invoke operations from
classes that perform these operations.
o Open/Closed Principle: You can introduce new commands into the app without
breaking existing client code.
o You can implement undo/redo.
o You can implement deferred execution of operations.
o You can assemble a set of simple commands into a complex one.

Disadvantages:
o The code may become more complicated since you’re introducing a whole new layer
between senders and receivers.

Structure:
14. command structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <iostream>
#include <string>
using namespace std;
class Person;

class Command
{

Person *object; //
void(Person:: *method)();
public:
Command(Person *obj = 0, void(Person:: *meth)() = 0)
{
object = obj; // the argument’s name is “meth”
method = meth;
}
void execute()
{
(object->*method)(); // invoke the method on the object
}
};

class Person
{
string name;
Command cmd;
public:
Person(string n, Command c): cmd©
{
name = n;
}
void talk()
{
// “this” is the sender, cmd has the receiver
cout << name << “ is talking” << endl;
cmd.execute(); // ask the “black box” to callback the receiver
}
void passOn()
{
cout << name << “ is passing on” << endl;

// 4. When the sender is ready to callback to the receiver,


// it calls execute()
cmd.execute();
}
void gossip()
{
cout << name << “ is gossiping” << endl;
cmd.execute();
}
void listen()
{
cout << name << “ is listening” << endl;
}
};

int main()
{
Person wilma(“Wilma”, Command());

Person betty(“Betty”, Command(&wilma, &Person::listen));


Person barney(“Barney”, Command(&betty, &Person::gossip));
Person fred(“Fred”, Command(&barney, &Person::passOn));
fred.talk();
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd “/Users/kh
uenguyen/Desktop/haskell/” && g++ final.cpp -o final && “/Users/
khuenguyen/Desktop/haskell/”final
Fred is talking
Barney is passing on
Betty is gossiping
Wilma is listening

UML diagram:
15. Iterator

Definition:
A collection’s items can be traversed using an iterator, a behavioral design pattern, without
having to reveal the collection’s underlying representation (such as a list, stack, tree, etc.).

Goal:
Provides a way to access the elements of an aggregate object without exposing its underlying
representation.

Advantages:
o Single Responsibility Principle. You can clean up the client code and the collections
by extracting bulky traversal algorithms into separate classes.
o Open/Closed Principle. You can implement new types of collections and iterators and
pass them to existing code without breaking anything.
o You can iterate over the same collection in parallel because each iterator object
contains its own iteration state.
o For the same reason, you can delay an iteration and continue it when needed.

Disadvantages:
o Applying the pattern can be an overkill if your app only works with simple
collections.
o Using an iterator may be less efficient than going through elements of some
specialized collections directly.

Structure:
15. iterator structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <iostream>
#include <string>
#include <vector>
class Iterator;
class Container {
friend class Iterator;

public:
void Add(int a) {
m_data_.push_back(a);
}

private:
std::vector<int> m_data_;
};
class Iterator {
public:
typedef typename std::vector<int>::iterator iter_type;
Iterator(Container *con) :m_p_data_(con) {
m_it_ = m_p_data_->m_data_.begin();
}

void First() {
m_it_ = m_p_data_->m_data_.begin();
}
void Next() {
m_it_++;
}
bool IsDone() {
return (m_it_ == m_p_data_->m_data_.end());
}
iter_type Current() {
return m_it_;
}
private:
Container *m_p_data_;
iter_type m_it_;
};
Iterator *CreateIterator(Container* con) {
return new Iterator(con);
}
void ClientCode() {
std::cout << "________________Iterator with int______________________________________" << std::endl;
Container cont;
for (int i = 0; i < 10; i++) {
cont.Add(i);
}
Iterator *it = CreateIterator(&cont);
for (it->First(); !it->IsDone(); it->Next()) {
std::cout << *it->Current() << std::endl;
}
delete it;
}
int main() {
ClientCode();
return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
________________Iterator with int______________________________________
0
1
2
3
4
5
6
7
8
9

UML diagram:
16. Mediator

Definition:
Mediator is a behavioral design pattern minimize uncontrollable dependency between classes.
The pattern restricts direct communications between the objects and forces them to cooperate
only via a “mediator object”.

Goal:
Mediator pattern is used to reduce communication complexity between multiple objects or
classes.

Advantages:
o Single Responsibility Principle. You can extract the communications between various
components into a single place, making it easier to comprehend and maintain.
o Open/Closed Principle. You can introduce new mediators without having to change
the actual components.
o You can reduce coupling between various components of a program.
o You can reuse individual components more easily.

Disadvantages:
o Over time a mediator can evolve into a “God” Object.

Structure:
16. mediator structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
struct ChatRoom {
virtual void broadcast(string from, string msg) = 0;
virtual void message(string from, string to, string msg) = 0;
};
struct Person {
string m_name;
ChatRoom* m_room{nullptr};
vector<string> m_chat_log;

Person(string n) : m_name(n) {}

void say(string msg) const { m_room->broadcast(m_name, msg); }


void pm(string to, string msg) const { m_room->message(m_name, to, msg); }
void receive(string from, string msg) {
string s{from + ": \"" + msg + "\""};
cout << "[" << m_name << "'s chat session]" << s << "\n";
m_chat_log.emplace_back(s);
}
};
struct GoogleChat : ChatRoom
{
vector<Person*> m_people;

void broadcast(string from, string msg) {


cout<<"---------------------\n";
for (auto p : m_people)
if (p->m_name != from)
p->receive(from, msg);
cout<<"---------------------\n";
}

void join(Person *p) {


string join_msg = p->m_name + " joins the chat";
broadcast("room", join_msg);
p->m_room = this;
m_people.push_back(p);
}

void message(string from, string to, string msg) {


cout<<"---------------------\n";
auto target = find_if(begin(m_people), end(m_people),
[&](const Person *p) {
return p->m_name == to;
cout<<"---------------------\n";
});

if (target != end(m_people)) (*target)->receive(from, msg);


cout<<"---------------------\n";
}
};
int main() {
GoogleChat room;
Person jimmy{"Jimmy"};
Person simon{"Simon"};
room.join(&jimmy);
room.join(&simon);
jimmy.say("hi room");
simon.say("oh, hey john");
Person donald{"Donald"};
room.join(&donald);
donald.say("hi everyone!");
simon.pm("Simon", "glad you found us, Donald!");
return EXIT_SUCCESS;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ memento.cpp -o memento && "/Us
ers/khuenguyen/Desktop/haskell/"memento
---------------------
---------------------
---------------------
[Jimmy's chat session]room: "Simon joins the chat"
---------------------
---------------------
[Simon's chat session]Jimmy: "hi room"
---------------------
---------------------
[Jimmy's chat session]Simon: "oh, hey john"
---------------------
---------------------
[Jimmy's chat session]room: "Donald joins the chat"
[Simon's chat session]room: "Donald joins the chat"
---------------------
---------------------
[Jimmy's chat session]Donald: "hi everyone!"
[Simon's chat session]Donald: "hi everyone!"
---------------------
---------------------
[Simon's chat session]Simon: "glad you found us, Donald!"
---------------------

UML diagram:

17. Memento

Definition:
A behavioral design pattern called Memento allows you to preserve and restore an object's prior
state without disclosing the specifics of how it was implemented.

Goal:
Without invalidating encapsulation, capture and externalize an object’s internal state.

Advantages:
o You can produce snapshots of the object’s state without violating its encapsulation.
o You can simplify the originator’s code by letting the caretaker maintain the history of
the originator’s state.

Disadvantages:
o The app might consume lots of RAM if clients create mementos too often.
o Caretakers should track the originator’s lifecycle to be able to destroy obsolete
mementos.
o Most dynamic programming languages, such as PHP, Python and JavaScript, can’t
guarantee that the state within the memento stays untouched.
Structure:

17. memento structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
#include<string>
#include<vector>
using namespace std;
class Memento{
private:
string state;
string date;
public:
Memento(string stat) : state(stat){
this->state=stat;
time_t now =time(NULL);
date = ctime(&now);
}
string State(){
return this->state;
}
string Date(){
return this->date;
}
string getInfo(){
return this->date + " / (" + this->state + ")";
}
};
class Logger{
private:
string note;
public:
Logger(string noting) : note(noting){
cout<<"A new logger is created!\nLog: "<<noting<<endl;
}
void logging(){
cout<<"Something important is going to be done!\n";
string state;cin>>state;
this->note=state;
cout<<"New log: "<<state<<endl;
}
Memento* save(){
return new Memento(this->note);
}
void res(Memento* m){
this->note=m->State();
cout<<"My note has changed to: "<<this->note<<endl;
}
};
class CareTaker{
vector<Memento*> mementos;
Logger *l;
public:
CareTaker(Logger* logger) : l(logger){
}
~CareTaker(){
for(auto x:mementos) delete x;
}
void backUp(){
cout<<"Saving note...\n";
mementos.push_back(this->l->save());
}
void undo(){
if(this->mementos.empty()){
return;
}
Memento* m = this->mementos.back();
mementos.pop_back();
cout<<"Restoring note to: "<<m->getInfo()<<endl;
try{
this->l->res(m);
} catch(...){
this->undo();
}
}
void his() const{
cout<<"CareTaker: here is the list of logs:\n";
for(auto x : this->mementos){
cout<<x->getInfo()<<endl;
}
}
};
int main(){
Logger * origin = new Logger("Hello World");
CareTaker* care = new CareTaker(origin);
care->backUp();
origin->logging();
care->backUp();
origin->logging();
care->backUp();
origin->logging();
care->his();
cout<<"Let's go to previous Logs!\n\n";
care->undo();
cout<<"Once more!\n\n";
care->undo();
return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ memento.cpp -o memento && "/Us
ers/khuenguyen/Desktop/haskell/"memento
A new logger is created!
Log: Hello World
Saving note...
Something important is going to be done!
OK
New log: OK
Saving note...
Something important is going to be done!
KO
New log: KO
Saving note...
Something important is going to be done!
NO
New log: NO
CareTaker: here is the list of logs:
Sat Jun 10 07:30:20 2023
/ (Hello World)
Sat Jun 10 07:30:22 2023
/ (OK)
Sat Jun 10 07:30:24 2023
/ (KO)
Let's go to previous Logs!

Restoring note to: Sat Jun 10 07:30:24 2023


/ (KO)
My note has changed to: KO
Once more!

Restoring note to: Sat Jun 10 07:30:22 2023


/ (OK)
My note has changed to: OK

UML diagram:
18. Observer

Definition:
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.

Goal:
Defines a one-to-many relationship between objects. It enables an item, referred to as the subject,
to alert other objects, referred to as observers, of any status changes. Then, based on their own
logic code, the observers can respond to these changes.

Advantages:
o Open/Closed Principle. You can introduce new subscriber classes without having to
change the publisher’s code (and vice versa if there’s a publisher interface).
o You can establish relations between objects at runtime.

Disadvantages:
o Subscribers are notified in random order.

Structure:
18. observer structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
#include<vector>
using namespace std;
class Observer{
public:
virtual void noti() =0;
};
class Student{
private:
vector<Observer*> list;
public:
void regis(Observer* a){
list.push_back(a);
}
void notifyObservers(){
for (auto x : list) x->noti();
}
};
class Teacher : public Observer{
public:
Teacher(Student& a){
a.regis(this);
}
void noti() override{
cout<<”Teacher is notified\n”;
}
};
class Parent : public Observer{
public:
Parent(Student& a){
a.regis(this);
}
void noti() override{
cout<<”Parent is notified\n”;
}
};
int main(){
Student studentA;
Parent parentA(studentA);
Teacher teacherA(studentA);
studentA.notifyObservers();
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd “/Users/kh
uenguyen/Desktop/haskell/” && g++ final.cpp -o final && “/Users/
khuenguyen/Desktop/haskell/”final
Parent is notified
Teacher is notified

UML diagram:

19. State

Definition:
State is a behavioral design pattern that lets an object alter its behavior when its internal state
changes, appearing as if the object changed its class.

Goal:
The state design pattern’s primary objective is to enable objects to adapt their behavior based on
the current state. The state information is established at runtime and is modifiable there, allowing
behavior to be altered dynamically and giving the impression that the object has changed classes.
Advantages:
o Single Responsibility Principle. Organize the code related to particular states into
separate classes.
o Open/Closed Principle. Introduce new states without changing existing state classes
or the context.
o Simplify the code of the context by eliminating bulky state machine conditionals.

Disadvantages:
o Applying the pattern can be overkill if a state machine has only a few states or rarely
changes.

Structure:

19. state structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include <iostream>
#include <typeinfo>
class Allien;
class Idenity {
protected:
Allien *allien;
public:
virtual ~Idenity() {
}
void set_alien(Allien *a) {
this->allien = a;
}
virtual void greeting() =0;
virtual void name() = 0;
virtual void shapeShift() =0;
};
class Allien {
private:
Idenity *state_;
public:
Allien(Idenity *state) : state_(nullptr) {
this->TransitionTo(state);
}
~Allien() {
delete state_;
}
void TransitionTo(Idenity *state) {
std::cout << "Shape shifting to "; state->name(); std::cout<<".\n";
if (this->state_ != nullptr) delete this->state_;this->state_ = state;this->state_->set_alien(this);
}
void Do1() { this->state_->greeting();}
void Do2() {this->state_->shapeShift();}
};
class GWEN : public Idenity{
void name() override{ std::cout << "GWEN\n";}
void greeting() override{ std::cout << "Hi, this is GWEN!\n";}
void shapeShift() override;
};
class BOB : public Idenity {
public:
void name() override {std::cout << "BOB\n";}
void greeting() override{std::cout << "Hi, this is BOB!\n";}
void shapeShift() override{
{ std::cout << "BOB is doing something\n"<<"Allien wants to shape shift.\n";this->allien->TransitionTo(new GWEN);}
}
};
void GWEN::shapeShift(){
std::cout << "GWEN is doing something\n"<< "Allien wants to shape shift.\n";this->allien->TransitionTo(new BOB);
}

int main() {
Allien *agent = new Allien(new BOB);agent->Do1();agent->Do2();agent->Do1();delete agent;return 0;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
Shape shifting to BOB
.
Hi, this is BOB!
BOB is doing something
Allien wants to shape shift.
Shape shifting to GWEN
.
Hi, this is GWEN!

UML diagram:
20. Strategy

Definition:
Strategy is a behavioral design pattern that makes it possible to define a family of algorithms by
classifying them separately and allowing for interchangeability of the objects.

Goal:
Isolate the code, internal data, and dependencies of various algorithms from the rest of the code.
Different clients have a straightforward interface to use to run the algorithms and swap them as
needed.

Advantages:
o You can swap algorithms used inside an object at runtime.
o You can isolate the implementation details of an algorithm from the code that uses it.
o You can replace inheritance with composition.
o Open/Closed Principle. You can introduce new strategies without having to change
the context.

Disadvantages:
o This pattern becomes overly complicated with new classes and interfaces if you only
have a couple of algorithms and they rarely change.
o Clients must be aware of the differences between strategies to be able to select a
proper one.
o Functions can be used instead of these “Strategy objects”, which does not require a
bulk amount of code for classes and interfaces.

Structure:
20. strategy structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
using namespace std;
class Strategy{
public:
virtual void exe()=0;
virtual ~Strategy(){}
};
class Pikachu{
Strategy* strategy;
public:
void set(Strategy* a){
this->strategy = a;
}
void attack(){
if(this->strategy) strategy->exe();
}
};
class HighVoltage : public Strategy{
void exe(){
cout<<"Pikachu release high voltage!!!\n";
}
};
class SpinAttack : public Strategy{
void exe(){
cout<<"Pikachu uses spinning attack!!!\n";
}
};
int main(){
Pikachu a;
a.set(new HighVoltage);
a.attack();
a.set(new SpinAttack);
a.attack();
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
Pikachu release high voltage!!!
Pikachu uses spinning attack!!!

UML diagram:

21. Template method

Definition:
Template Method is a behavioral design pattern that allows subclasses to modify certain
algorithmic behaviors without altering its code structure while still defining the algorithm's
prototype in the superclass.

Goal:
Define the steps of an algorithm within a single method of a class. This allows you to constrain
specific operations of an application to a single method.

Advantages:
o You can let clients override only certain parts of a large algorithm, making them less
affected by changes that happen to other parts of the algorithm.
o You can pull the duplicate code into a superclass.
Disadvantages:
o Some clients may be limited by the provided skeleton of an algorithm.
o You might violate the Liskov Substitution Principle by suppressing a default step
implementation via a subclass.
o Template methods tend to be harder to maintain the more steps they have.

Structure:

21. template method structure (https://refactoring.guru/design-patterns)

Code example:

Source:
#include<iostream>
using namespace std;
class Predator{
public:
virtual void Prepare(){
cout<<”Predator preparing…”;
}
virtual void Attack(){
cout<<”Predator attack…”;
}
};
class Lion : public Predator{
public:
void Prepare(){
cout<<”Lion shaperning teeth\n”;
}
void Attack(){
cout<<”Lion charges forward\n”;
}
};
class Aligator : public Predator{
public:
void Prepare(){
cout<<”Aligator approaching\n”;
}
void Attack(){
cout<<”Alogator uses death roll\n”;
}
};
int main(){
Aligator a;
Lion l;
cout<<”Lion’s turn\n”;
l.Prepare();
l.Attack();
cout<<”Aligator’s turn\n”;
a.Prepare();
a.Attack();
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd “/Users/kh
uenguyen/Desktop/haskell/” && g++ final.cpp -o final && “/Users/
khuenguyen/Desktop/haskell/”final
Lion’s turn
Lion shaperning teeth
Lion charges forward
Aligator’s turn
Aligator approaching
Alogator uses death roll

UML diagram:
22. Visitor

Definition:
Visitor is a behavioral design pattern that lets you separate algorithms from the objects they
operate on.

Goal:
Define a new operation without introducing the modifications to an existing object code
structure.

Advantages:
o Open/Closed Principle. You can introduce a new behavior that can work with objects
of different classes without changing these classes.
o Single Responsibility Principle. You can move multiple versions of the same behavior
into the same class.
o A visitor object can accumulate some useful information while working with various
objects. This might be handy when you want to traverse some complex object
structure, such as an object tree, and apply the visitor to each object of this structure.

Disadvantages:
o You need to update all visitors each time a class gets added to or removed from the
element hierarchy.
o Visitors might lack the necessary access to the private fields and methods of the
elements that they’re supposed to work with.

Structure:

22. visitor structure (https://refactoring.guru/design-patterns)


Code example:

Source:
class Square;
class Circle;
#include<iostream>
using namespace std;
struct shapeVisitor{
public:
virtual void visit (const Square& ) =0;
virtual void visit (const Circle&) =0;
};
class Shape{
virtual void accept(shapeVisitor&) const = 0;
};
class Square : public Shape{
public:
void accept(shapeVisitor& a) const override{
a.visit(*this);
}
};
class Circle : public Shape{
public:
void accept(shapeVisitor& a) const override{
a.visit(*this);
}
};
class shapePrinter : public shapeVisitor{
public:
void visit(const Square&) override { std::cout << "this is Square from visitor\n"; }
void visit(const Circle&) override { std::cout << "this is Circle from visitor\n"; }
};
int main(){
shapePrinter printer;
Square a;
Circle b;
printer.visit(a);
printer.visit(b);
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
this is Square from visitor
this is Circle from visitor

UML diagram:
23. Interpreter

Definition:
Interpreter design pattern creates a representation of a language's grammar and an interpreter that
uses it to understand sentences in the language.

Goal:
Given a language, define a representation for its grammar along with an interpreter that uses the
representation to interpret sentences in the language.

Advantages:
o Extendable and modifiable: extending or making changes to the grammar becomes
highly convenient.
o Easy Implementation: This pattern represents grammar classes as syntax trees, where
each node represents a grammar rule. Each class has a similar implementation, so
once you create one class, a few changes can lead to other classes. Hence, the
implementation of this pattern is straightforward.
o Interpreting Expressions Differently: Expression classes define how the expression
will be evaluated.

Disadvantages:
o Complex grammar rules are hard to implement.

Structure:
23. interpreter structure
(https://sourcemaking.com/design_patterns/interpreter#:~:text=problem%20solving%20process.-,Structure,node%20in%20a%2
0tree%20structure).)

Code example:

Source:
#include<iostream>
#include<vector>
#include<string>
using namespace std;
struct Token {
enum Type { integer, plus, minus, lparen, rparen };
Type m_type;
string m_text;

Token(Type typ, const string& txt): m_type(typ), m_text(txt) {}

friend ostream& operator<<(ostream& os, const Token& o) { return os << "`" << o.m_text << "`"; }
};

vector<Token> lex(const string& input) {


vector<Token> result;

for (auto curr = begin(input); curr != end(input); ++curr) {


switch (*curr) {
case '+': result.emplace_back(Token::plus, "+"); break;
case '-': result.emplace_back(Token::minus, "-"); break;
case '(': result.emplace_back(Token::lparen, "("); break;
case ')': result.emplace_back(Token::rparen, ")"); break;
default:
auto first_not_digit = find_if(curr, end(input), [](auto c) {
return !isdigit(c);
});
string integer = string(curr, first_not_digit);
result.emplace_back(Token::integer, integer);
curr = --first_not_digit;
}
}
return result;
}

int main() {
auto tokens = lex("(13-4)-(12+1)");

for (auto& t: tokens)


cout << t << " ";

return EXIT_SUCCESS;
}

Output:
(base) khuenguyen@MacBook-Air-cua-Nguyen haskell % cd "/Users/kh
uenguyen/Desktop/haskell/" && g++ final.cpp -o final && "/Users/
khuenguyen/Desktop/haskell/"final
`(` `13` `-` `4` `)` `-` `(` `12` `+` `1` `)` %

UML diagram:

Section I.02 New Programming Approaches

(a) Data Wrangling


● Definition:
In data related programming, Data Wrangling is one of the most important aspects and is an
indispensable part of the data analysis process. Data wrangling is the process of converting raw
data (means data which was captured and had not been formatted or transformed) into a usable
form. It may also be called data munging or data remediation.
More specifically, data wrangling refers to a series of procedures intended to examine, organize,
and validate disorganized or chaotic raw datasets into meaningful datasets. Your wrangled data
may be used to generate insightful discoveries and direct business choices.

For example, let’s think of data wrangling in house building. In building houses, the builder
(engineer) uses the wood (data) to construct a desirable house (product, information). If the wood
is supplied in the form of raw wood (raw data, unprocessed), the builder will have to carve those
woods into pieces, and use the pieces to complete his job. This makes the role of the builder
extremely heavy thus decreasing efficiency and time. Therefore, he will need a machine or
someone else to do the wood processing for him. Similarly, a data scientist or data analyst will
need someone to do the data – preprocessing for them.

In general, data wrangling or munging composes of 4 steps:

1. Discovery
You'll effectively set yourself up for the remainder of the procedure during the discovery step.
Here, you'll consider the inquiries you wish to pursue and the kinds of information you will need
in order to do so. In order to determine how you'll clean, arrange, and organize your data in the
next steps, you'll also need to discover the data you intend to utilize and look at it in its current
state.

2. Transformation
During the transformation stage, you'll act on the plan you developed during the discovery stage.
This piece of the process can be broken down into four components: structuring, normalizing and
denormalizing, cleaning, and enriching.
Data structuring
Making sure that various datasets are in compatible formats is part of the data structuring
process. In this manner, the data you combine or merge will be in a format suitable for the
analytical model you intend to employ to analyze the data.
Normalizing and denormalizing data
Data normalization involves organizing your data into a coherent database and getting rid of
irrelevant or repetitive data. Denormalization involves combining multiple tables or relational
databases, making the analysis process quicker. Keep your analysis goal and business users in
mind as you think about normalization and denormalization.
Data cleaning
You eliminate mistakes that might skew or impair the accuracy of your analysis throughout the
cleaning procedure. This comprises operations like normalizing inputs, eliminating empty cells
or duplicate values, removing outliers, correcting errors, and dealing with biases. Making
ensuring the data is as error-free as feasible is the ultimate objective.
Enriching data
Once you've transformed your data into a more usable form, consider whether you have all the
data you need for your analysis. If you don't, you can enrich it by adding values from other
datasets. You also may want to add metadata to your database at this point.

3. Validation
During the validation step, you essentially check the work you did during the transformation
stage, verifying that your data is consistent, of sufficient quality, and secure. This step may be
completed using automated processes and can require some programming skills.

4. Publishing
After you've finished validating your data, you're ready to publish it. When you publish data,
you'll put it into whatever file format you prefer for sharing with other team members for
downstream analysis purposes.

● Importance of Data Wrangling


Data wrangling prepares your data for the data mining process, which is the stage of analysis
when you look for patterns or relationships in your dataset that can guide actionable insights.
The quality of your data analysis depends on the data itself. If you study faulty data, it's possible
that you will come to unreliable conclusions and be unable to make judgments based on solid
evidence. You can be more confident in the inferences you make from your data if the data has
been wrangled. Results will come much more quickly, and there will be less possibility for
mistakes or lost chances.

24. a visualization of Data Wrangling (source: https://favtutor.com/blogs/data-wrangling)

(b) Smart Contract

● Definition:
Smart contracts are simply programs stored by blockchain technology that automatically run
when predefined conditions are met. They are typically used to ensure the execution of an
agreement so that all participants can immediately be certain of the outcome, without any
intermediary’s involvement or time loss. They can also automate a workflow, triggering the next
action when some particular conditions are met.

For example, if you were a business man who signed a contract with a cooperating partner, and
during the time of your cooperation, your partner violates or admits to an action that would cause
a sequence of future actions that has been agreed by both sides, you do not actually know for
sure whether your partner is going to admit to the consequences. It could either be an accident
(maybe your partner forgot about it) or an intentional behavior (maybe they want to avoid
problems on their term). Either way, you wouldn’t be able to control that behavior or force your
partner to cooperate rightly. With smart contract, code that are stored using blockchain
technology will automatically run if any of the predefined term is met, without needing any
intervention or causing errors, mistakes.

25. a visualization of smart contract

● How Smart Contracts work:


Smart contracts work by following simple “if/when…then…” statements that are written into
code on a blockchain. A network of computers executes the actions when predetermined
conditions have been met and verified. These actions could include releasing funds to the
appropriate parties, registering a vehicle, sending notifications, or issuing a ticket. The
blockchain is then updated when the transaction is completed. That means the transaction cannot
be changed, and only parties who have been granted permission can see the results.

Within a smart contract, there can be as many stipulations as needed to satisfy the participants
that the task will be completed with satisfaction. To establish the terms, participants must
determine how transactions and their data are represented on the blockchain, agree on the
“if/when...then…” rules that govern those transactions, explore all possible exceptions, and
define a framework for resolving disputes. Then the smart contract can be programmed by a
developer – although increasingly, organizations that use blockchain for business provide
templates, web interfaces, and other online tools to simplify structuring smart contracts.

● Benefits of Smart Contract:


o Speed, efficiency and accuracy
Once a condition is met, the contract is executed immediately. Because smart contracts are
digital and automated, there’s no paperwork to process and no time spent reconciling errors that
often result from manually filling in documents.
o Trust and transparency
Because of no third-party involvement and sharing of encrypted records among all participants,
there’s no need to doubt whether terms and contracts have been altered for personal benefit.
o Security
Blockchain transaction records are encrypted, which makes them almost impossible to infiltrate
or hack into. Moreover, because each record is connected to the previous and subsequent records
on a distributed ledger, hackers would have to access and alter the entire chain to change a single
record.
o Savings
Smart contracts dispense the need for middlemen to conduct transactions, along with the costs
and wait times that associate with them.

● Problems with Smart Contract:


o Reliance on External Data Sources
Smart contracts are code that are design to run automatically when predetermined conditions are
met, it cannot communicate with the outside world when some conditions are needed from third
– party before the flow of execution can be started.
o Rigidity
Immutability means that the code in the protocols cannot be unilaterally changed once deployed
on the blockchain. This is one of the main advantages of smart contract, however, it does have
some negative effects: changes in terms of agreement can be extremely difficult and costly and
error fixing is also a major problem once it happen in smart contract.
o Confidentiality of Information
All information on the blockchain is accessible to everyone since nodes hold a copy of the
blockchain's history. But not everyone wants their confidential contractual information to be
shown out.
o Legal status
Until now, there is still no official laws to protect the legality of smart contracts, meaning there is
no solid way to enforce smart contracts. The lack of legal protection is perhaps the biggest
disadvantage of using smart contracts at the moment.
o Security flaws
Similar to any other software programs, smart contracts can – and will often develop bugs. The
huge difference here is that, bugs that happen in smart contracts will certainly result in much
higher consequences (time and cost). Any loophole in the system would be taken advantage by
malicious hacker and will cause tremendous lost.
o Simplistic operation
The simplicity of smart contracts cannot cover various ambiguous terms that cannot be written
into binary code. Most of those conditions must be seen and tested in real life and simply code
and software will not be able to interpret.

● Application of Smart Contract:


Nowadays, Smart Contract is a strong growing field and has been applied into many industries
because of its reliability and benefits. Some of the most popular application of smart contracts
that have been shown are safeguarding the efficacy of medications, increasing trust in
retailer-supplier relationships, making international trade faster and more efficient.

You might also like