hvn-network

My Blog List

Tuesday, November 1, 2016

Simple Factory Pattern, Factory Method Pattern, Abstract Factory Pattern

Factory pattern is one of the most used pattern in Object Oriented Design. In this post, i will present basic concepts about Simple Factory Pattern, Factory Method Pattern and Abstract Factory Pattern. Then i will show the differences among them and when we use each of them. 

1. Problem description

Imagine that you was assigned to develop an application to sell mobile phones. At first, our shop sell brand new Apple phones and brand new Samsung phones only. We create a Phone abstract class inherited by concrete classes. Name and pack methods inply the phone name and the phone is packed respectively. A client (main function) create phone based on user's input. 

Class diagram for the example

The C++ code for the above description can be given as follows. 

#include <iostream>
#include <string>
using namespace std;
class Phone {
public:
 virtual void name() = 0;
 void pack() {
  cout << "This phone is packed and sent to you soon" << endl;
 }
};

class Samsung : public Phone {
public:
 void name() {

  cout << "This is a brand new Samsung phone" << endl;
 }
};

class Apple : public Phone {
public:
 void name() {
  cout << "This is a brand new Iphone" << endl;
 }
};

/* Client code */
int main() {
 string userInput = "samsung";  //simulate user input
 Phone *phone;
 if (userInput == "samsung") {
  phone = new Samsung;
 } else {
  phone =  new Apple;
 }
 phone ->name();
 phone ->pack();
 return 0;
}
Assume that an user wants to buy a Samsung phone (we simulate this action with string userInput = "samsung";) , the output will be: 
"This is a brand new Samsung phone
This phone is packed and sent to you soon".

In small and simple problem, this design work fine. But later, the shop decides to sell HTC phone. We have to modify client code (User interface). This breaks the Open Closed Principle, which says software entities (class, function, ...) should be open for extension and closed for modification). 

Lesson learnt, changes are obvious part of software development, but system should be developed to adapted with changes without modifying the tested sections.


2. Simple Factory Pattern.

We encapsulate the code sections should be changed (because of new phone type addition). So this modification does not affect the tested part of our software. To do that, we create a Simple Factory class to make a new Phone. Client UI code is unchanged even if new products are added.

Class diagram of simple factory pattern in theory

Class diagram of simple factory pattern for the example
The C++ code for this design is given as follows.

#include <iostream>
#include <string>
using namespace std;
class Phone {
public:
 virtual void name() = 0;
 void pack() {
  cout << "This phone is packed and sent to you soon" << endl;
 }
};


class Samsung : public Phone {
public:
 void name() {
  cout << "This is a brand new Samsung phone" << endl;
 }
};


class Apple : public Phone {
public:
 void name() {
  cout << "This is a brand new Iphone" << endl;
 }
};

class PhoneFactory {
public:
 Phone* makePhone(string userInput) {
  Phone *phone;
  if (userInput == "samsung") {
   phone = new Samsung;
  } else {
   phone =  new Apple;
  }
  phone ->name();
  phone ->pack();
  return phone;
 }
};

int main() {
 string userInput = "samsung";        /*simulate user input*/
 PhoneFactory *phoneFactory = new PhoneFactory;
 Phone *phone = phoneFactory->makePhone(userInput);
 return 0;

}

The shop operation has increased a lot and the boss wants to sell like new smart phones. Then, the code in PhoneFactory class can be modified as follows.

class PhoneFactory {
public:
 Phone* makePhone(string userInput) {
  Phone *phone;
  if (userInput == "samsung") {
   phone = new Samsung;
  } else if(userInput == "apple") {
   phone =  new Apple;
  } else if(userInput == "like new samsung){
   phone = new LikeNewSamsung;
  } else {
   phone = new LikeNewApple;
  }
  phone ->name();
  phone ->pack();
  return phone;
 }
};
It actually works, but breaks Single Responsibility Principle saying a class should have only one reason to change. Here,. the PhoneFactory class will be changed

  • When others brand new phones are added
  • When others like new phones are added
To solve this problem, we may think about are two separate Factory classes, one for brand new phones, the other for like new phones.
class BrandNewPhoneFactory {
public:
 Phone* makePhone(string userInput) {
  Phone *phone;
  if (userInput == "samsung") {
   phone = new Samsung;
  } else {
   phone =  new Apple;
  }
  phone ->name();
  phone ->pack();
  return phone;
 }
};
class LikeNewPhoneFactory{
  Phone *phone;
  if (userInput == "samsung") {
   phone = new LikeNewSamsung;
  } else {
   phone =  new LikeNewApple;
  }
  phone ->name();
  phone ->pack();
  return phone;
 }
};
This seems to work, but the two factory classes are independent. We cannot make sure every factory class follow the same rule.

3. Factory method pattern

To solve this problem, we use the factory method pattern, which let the abstract factory class encapsulate common functionalities and the derived classes will override the abstract method. 
Class diagram of factory method pattern in theory
Class diagram of factory method pattern for the example

For now, if a new phone group is add. a new factory will be derived from BasePhoneFactory, encapsulating creation of all concrete classes.

#include <iostream>
#include <string>
using namespace std;

class Phone {
public:
 virtual void name() = 0;
 void pack() {
  cout << "This phone is packed and sent to you soon" << endl;
 }
};


class Samsung : public Phone {
public:
 void name() {
  cout << "This is a brand new Samsung phone" << endl;
 }
};

class Apple : public Phone {
public:
 void name() {
  cout << "This is a brand new Iphone" << endl;
 }
};

class LikeNewSamsung: public Phone {
public:
 void name() {
  cout << "This is a like new Samsung phone"<< endl;
 }
};

class LikeNewApple: public Phone {
public:
 void name() {
  cout << "This is a like new Apple phone"<< endl;
 }
};


class PhoneFactory {
public:
 Phone *makePhone(string userInput) {
  Phone *phone = this -> getPhone(userInput);
  phone->name();
  phone->pack();
  return phone;
 }
 virtual Phone *getPhone(string userInput) = 0;
};

class BrandNewPhoneFactory : public PhoneFactory{
public:
 Phone *getPhone(string userInput) {
  if (userInput == "samsung") {
   return new Samsung;
  } else {
   return  new Apple;
  }
 }
};

class LikeNewPhoneFactory : public PhoneFactory{
public:
 Phone *getPhone(string userInput) {
  if (userInput == "samsung") {
   return new LikeNewSamsung;
  } else {
   return new LikeNewApple;
  }
 }
};


int main() {
 string userInput = "samsung";        /*simulate user input*/
 PhoneFactory *phoneFactory = new LikeNewPhoneFactory;
 Phone *phone = phoneFactory->makePhone(userInput);
 return 0;
}

4. Factory Method

Different from simple factory and factory method, which create one product at a time, factory method pattern is used when we want to create a family of components (usually to make a product by them). For example, we have to develop a software to assemble phones based on requirements of customers. For simplicity, we assume that phones are made from two main components Board and Monitor. We have three phone types:
  • Expensive phone: Created from expensive board and expensive monitor
  • Medium phone: Created from expensive board and cheap monitor
  • Cheap phone: Created from cheap board and cheap monitor
We define a AbstractFactory as a contract of derived class with two abstract methods: makeBoard and makeMonitor. Three derived class based on phone quality create the corresponding components.

Class diagram of Abstract Factory Pattern in theory 
Class diagram of Abstract Factory Pattern for the example
The C++ code for this example is given as follows.
#include <iostream>
#include <string>
using namespace std;


class Board {
public:
 virtual void showBoard() = 0;
};

class ExpensiveBoard : public Board {
public:
 void showBoard() {
  cout << "this is an expensive board" << endl;
 }
};

class CheapBoard : public Board {
public:
 void showBoard() {
  cout << "This is a cheap board" << endl;
 }
};

class Monitor {
public:
 virtual void showMonitor() = 0;
};

class ExpensiveMonitor : public Monitor {
public:
 void showMonitor() {
  cout << "This is an expensive monitor" << endl;
 }
};

class CheapMonitor : public Monitor {
public:
 void showMonitor() {
  cout << "This is a cheap monitor" << endl;
 }
};

class AbstractFactory {
public:
 virtual Board *makeBoard() = 0;
 virtual Monitor *makeMonitor() = 0;
};

class ExpensivePhoneFactory : public AbstractFactory {
public:
 Board *makeBoard() {
  return new ExpensiveBoard;
 }
 Monitor *makeMonitor() {
  return new ExpensiveMonitor;
 }
};

class MediumPhoneFactory : public AbstractFactory {
public:
 Board *makeBoard() {
  return new CheapBoard;
 }
 Monitor *makeMonitor() {
  return new ExpensiveMonitor;
 }
};

class CheapPhoneFactory : public AbstractFactory {
public:
 Board *makeBoard() {
  return new ExpensiveBoard;
 }
 Monitor *makeMonitor() {
  return new ExpensiveMonitor;
 }
};

class PhoneAssembler {
private:
 AbstractFactory *factoryType;
public:
 typedef enum {
  EXPENSIVE_PHONE,
  MEDIUM_PHONE,
  CHEAP_PHONE
 } PHONE_TYPE;
 PhoneAssembler(PHONE_TYPE type) {
  if (type == EXPENSIVE_PHONE) {
   factoryType = new ExpensivePhoneFactory;
  } else if (type == MEDIUM_PHONE) {
   factoryType = new MediumPhoneFactory;
  } else {
   factoryType = new CheapPhoneFactory;
  }
 }
 void assemble() {
  Board *board = factoryType->makeBoard();
  Monitor *monitor = factoryType->makeMonitor();
  board->showBoard();
  monitor->showMonitor();
  cout << "The phone is assembled" << endl;
 }
};

int main() {
 PhoneAssembler::PHONE_TYPE userInput = PhoneAssembler::EXPENSIVE_PHONE;        /*simulate user input*/
 PhoneAssembler *phoneAssembler = new PhoneAssembler(userInput);
 phoneAssembler->assemble();
 return 0;
}

5. Conclusion

We have learnt Simple Factory, Factory Method and Abstract Factory patterns. When we use each of them.
  • Simple factory pattern: when we have only one family of objects (E.g. create one phone from brand new phone family)
  • Factory method pattern: When we have multiple families of objects (E.g. create one phone from brand new phone family or like new phone family)
  • Abstract factory pattern: When we have multiple families of object components (E.g. Create one board and one monitor from board family and monitor family). 
Source code of the above examples can download in here.

6. References

Monday, October 31, 2016

Program to interface not implementation

In Object-Oriented Programming (OOP), the desired software is not only runnable but also extensible, maintainable and testable. In this post, I will present a principle in OOP: "Program to interface not implementation". Before digging deeper in this principle, i will present basic concepts about concrete class, abstract class and interface.

1. Basic concepts

1.1. Concrete class

Concrete class is a class that can be used to specify any specific entity. For example, class Car:
class Car{
 string color;
 string model;
public:
 string getColor(){
  return this->color;
 }
 void setColor(string color){
  this->color = color; 
 } 
 string getModel(){
  return this->model;
 }
 void setModel(string model){
  this->model = model;
 }
}

The class Car has two attributes color and model, so any object instantiated by class Car will be determistic with these attributes.

1.2. Abstract class

Suppose that we add a new method called getTax() to class Car. But this class does not know how to implement this because tax may be depended on car type (assuming car has two types: RegularCar and SupperCar). So we mark virtual keyword for the getTax() function. The class Car now is considered to be abstract.

class Car{
 string color;
 string model;
public:
 string getColor(){
  return this->color;
 }
 void setColor(string color){
  this->color = color; 
 } 
 string getModel(){
  return this->model;
 }
 void setModel(string model){
  this->model = model;
 }
 virtual float getTax();
}
We cannot use abstract class directly and the classes inheriting from this class have to provide implementation of the virtual methods.
class SportCar : public Car{
public:
 float getTax(){
   return 2000f;
 }
}

1.3. Interface

In C++ we may consider an interface as a pure abstract class (with no implementation code). For example: 
class Shape{
public:
 virtual float getArea();
 virtual float getPerimeter();
}

class Square : public Shape{
 int sideLength;
public:
 float getArea(){
  return sideLength*sideLength;
 }
 float getPerimeter(){
  return 4*sideLength;
 }
}

1.4. Why do we need Interfaces and Abstract Classes

We use interfaces and abstract class when:
 - We need multiple class to behave in a polymorphic way
 - We need some kind off contract to enforce on the classes.

2. Program to an interface, not an implementation 

This principle means uses interfaces from other part of the application rather than the concrete implementation. When we work on a software, we often spend much time maintaining than developing. If we programming to an implementation, when the requirement change, we need much more efforts in modifying code. For example:
class Dog{
public:
 void run(){
  cout <<"A dog is running";
 }
}

Class Collection{
public: 
 void performAnimalAction(){
  Dog *dog = new Dog;
  dog -> run();
 }
}
This example, use concrete implementation (Dog), it work fine if we has only Dog in Collection. Imagine that we add a new class called Cat to the class Collection later, the performAnimalAction() function will be messed up.

To handle this problem,  we create a interface Animal, and use interface instead of concrete implementation in performAnimalAction() function.

class Animal{
public:
 virtual void run();
}

class Dog : public Animal{
public:
 void run(){
  cout <<"A dog is running";
 }
}

class Cat : public Animal{
public:
 void run(){
  cout <<"A cat is running";
 }
}
class Collection{
public: 
 void performAnimalAction(Animal *_animal){
  Animal *animal = _animal;
  animal->run();
 }
}

int main(){
 Collection *collection = new Collection;
 collection->performAnimalAction(new Dog);
 collection->performAnimalAction(new Cat);
}

Reference

Wednesday, October 26, 2016

Packet flow in routing/switching





Situation 1: CPE1 talks with CPE2

- Assume at CPE1: ping 192.168.0.4

CPE1 checks its routing table to see where to send packet to. It detects that CPE2 is on same network.

Header of sending packet from CPE1.
SIP = 192.168.0.3, DIP = 192.168.0.4
SA = m1, DA=? (unknown yet)

CPE1 broadcasts ARP to know MAC address of 192.168.0.4. After this step it knows DA as m2. Consequently, Switch1 updates its learning table
MAC (m1) ------ Port (fe1)
MAC (m2) ------ Port (fe2)

At this time, CPE1 just sends packet to CPE2 with below header.
SIP = 192.168.0.3, DIP = 192.168.0.4
SA = m1, DA=m2

Situation 2: CPE1 talks with CPE3
- Assume at CPE1: ping 192.168.11.5

CPE1 checks its routing table to see where to send packet to. It detects that CPE3 is not on same network. It has to send packet to its gateway (192.168.0.1) at interface R1(Lan1)

Header of sending packet from CPE1.
SIP = 192.168.0.3, DIP = 192.168.11.5
SA = m1, DA=? (unknown yet)

CPE1 broadcasts ARP to know MAC address of 192.168.0.1. After this, CPE1 know MAC of gateway as a1. Simultaneously, Switch1 updates its learning table.
MAC (m1) ------ Port (fe1)
MAC (m2) ------ Port (fe2)
MAC (a1) ------ Port (fe0)

CPE1 then sends packet to gateway with below header
SIP = 192.168.0.3, DIP = 192.168.11.5
SA = m1, DA=a1


Next, Router1 receives the packet, it check DIP (192.168.11.5) in its routing table to see where to forward packet. Ok, it detects that the packet should be forwarded to interface R2(Lan2, 192.168.11.1).

Router1 creates below packet.
SIP = 192.168.0.3, DIP = 192.168.11.5
SA = a2, DA=?

Now, it broadcasts ARP to know MAC address of 192.168.11.5. After this, Router1 knows MAC of 192.168.11.5 as m3. Switch2 updates learning table as
MAC (m3) ------ Port (fe1)
MAC (a2) ------ Port (fe0)

Finally, Router1 sends packet to CPE3 as below header.
SIP = 192.168.0.3, DIP = 192.168.11.5
SA = a2, DA= m3

Take note
- Switch just works at L2 (MAC address)
- Router is to transmit packets between different networks based on routing table (L3, IP address).

Routing in router:
+ Static routing (route add ...)
+ Dynamic routing (using protocol such as RIP, which broadcasts periodically its routing table to neighborhood and update the best routes).

Q&A
1. Why Home Gateway doesn't need to use RIP protocol?

Thursday, October 6, 2016

Sunday, October 2, 2016

I/O Multiplexing

Mô hình I/O multiplexing được tóm tắt như sau:



















Các hàm quan trọng:
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct *timeout);

void FD_CLR(int fd, fd_set *fdset); //xóa bit tương ứng trong fdset

//sử dụng hàm này sau select để kiểm tra bit tương ứng trong fdset là được set hoặc không
int FD_ISSET(int fd, fd_set *fdset); 

void FD_SET(int fd, fd_set *fdset); //set bit trong *fdset ứng với fd

void FD_ZERO(fd_set *fdset); // xóa tất cả bit trong fdset
Nếu timeout là NULL, select bị block đến khi tồn tại fd có dữ liệu. Nếu timeout là 0, select trả lại lập tức sau khi kiểm tra các fd. Nếu timeout khác NULL và 0, select() returns khi một hoặc nhiều file discriptors trong các tập readset, wirteset, exceptset có dữ liệu cho I/O hoặc hết timeout.

Nếu trả lại thành công, select trả lại số fds có dữ liệu sẵn sàng.

Nếu không thành công, select trả lại -1 và errno. Một số errno như sau:
- EBADF: có một fd không hợp lệ
- EINTR: select bị ngắt bởi tín hiệu trước khi timeout
- EINVAL: khoảng timeout không hợp lệ

Implementation của fdset dựa trên 1 bit mask kiểu số nguyên, hoặc bit fields trong mảng số nguyên.


Ví dụ sau về 1 hàm bị block cho tới khi 1 trong 2 fd sẵn sàng có dữ liệu.


#include errno.h
#include sys/select.h

int whicisready(int fd1, int fd2) {
 int maxfd;
 int nfds;
 fd_set readset;
 
 if (fd1 < 0 || fd1 >= FD_SETSIZE || fd2 < 0 || fd2 >= FD_SETSIZE)
 {
  errno = EINVAL;
  return -1;
 }
 
 maxfd = (fd1 > fd2) ? fd1: fd2;
 FD_ZERO(&readset);
 FD_SET(fd1,&readset);
 FD_SET(fd2,&readset);
 ndfs = select(maxfd+1, &readset, NULL, NULL, NULL);
 if (ndfs == -1)
  return -1;
 if (FD_ISSET(fd1, &readset))
  return fd1;
 if (FD_ISSET(fd2, &readset))
  return fd2;
 
 errno = EINVAL;
 return -1;
}




Friday, September 23, 2016

[C Programming] Exercises on Types, Operators and Expressions

We cover solution of exercises on types, operators and expressions in book "The C programming language".

Exercise 2-1. Write a program to determine the ranges of char, short, int, and long variables, both signed and unsigned, by printing appropriate values from standard headers and by direct computation. Harder if you compute them: determine the ranges of the various floating-point types.
Solution

Exercise 2-2. Write a loop equivalent to the for loop above without using && or ||.
Solution

Convert s to integer

Convert c to lower case

Pseudo-random generator

Exercise 2-3. Write a function htoi(s), which converts a string of hexadecimal digits (including an optional 0x or 0X) into its equivalent integer value. The allowable digits are 0 through 9, a through f, and A through F.
Solution

Squeeze

Strcat

Exercise 2-4. Write an alternative version of squeeze(s1,s2) that deletes each character in s1 that matches any character in the string s2.
Solution

Exercise 2-5. Write the function any(s1,s2), which returns the first location in a string s1 where any character from the string s2 occurs, or -1 if s1 contains no characters from s2. (The standard library function strpbrk does the same job but returns a pointer to the location.)
Solution

Exercise 2-6. Write a function setbits(x,p,n,y) that returns x with the n bits that begin at position p set to the rightmost n bits of y, leaving the other bits unchanged.
Solution

Exercise 2-7. Write a function invert(x,p,n) that returns x with the n bits that begin at position p inverted (i.e., 1 changed into 0 and vice versa), leaving the others unchanged.
Solution

Exercise 2-8. Write a function rightrot(x,n) that returns the value of the integer x rotated to the right by n positions.
Solution

Bit count

Exercise 2-9. In a two's complement number system, x &= (x-1) deletes the rightmost 1-bit in x. Explain why. Use this observation to write a faster version of bitcount.
Solution

Exercise 2-10. Rewrite the function lower, which converts upper case letters to lower case, with a conditional expression instead of if-else.
Solution