yeasir007

Showing posts with label FEATURED. Show all posts
Showing posts with label FEATURED. Show all posts

Saturday, October 13, 2018

UML case fundamental

UML case fundamental

UML(Unified Modeling Language) CASE Fundamental





Prerequisite:
Participants must have insight in the background of object-oriented techniques, and must have applied these techniques in object-oriented programming.For basic OOP Concept follow my post on Basic OOP Concept.


After completing this tutorial you will be able to:

  •  Understand why UML is used?
  •  Can work with the commonly used UML elements?
  •  Can create simple UML models?


Remember this tutorial doesn’t elaborately describe how to modeling a system from a problem domain rather it will describe how to understand UML of a problem domain.


What is modeling?

Before understanding UML (Unified Modeling Language), we need to understand what does modeling mean? Suppose, you are going to build a new addition to your house, you probably won’t start by just buying a bunch of wood and nailing it together until it looks about right. You’ll want some blueprints to follow so you can plan and structure the addition before you start working.



Modeling does the same thing for us in the software world. They are the blueprints for systems. Like a blueprint; a model also helps you plan a system before you build it. It can help you be sure the design is sound, the requirements have been met, and the system can withstand even a hurricane of requirement changes.


What is UML?

UML is one kind of modeling language for visualizing, specifying, constructing and documenting the artifacts of the system. It allows you to create a blue print of all aspects of the system, before actually physically implementing the system.

Why is it important?

Let's look at this question from the point of view of the construction trade. Architects design buildings. Builders use the designs to create buildings. The more complicated the building, the more critical the communication between architect and builder. Blueprints are the standard graphical language that both architects and builders must learn as part of their trade.

Writing software is not unlike constructing a building. The more complicated the underlying system, the more critical the communication among everyone involved in creating and deploying the software. In the past decade, the UML has emerged as the software blueprint language for analysts, designers, and programmers alike. It is now part of the software trade. The UML gives everyone from business analyst to designer to programmer a common vocabulary to talk about software design.



Types of UML diagram:

UML has nine kinds of modeling diagrams:
  •  Use case diagram
  •  Class diagrams 
  •  Object diagrams 
  •  Sequence diagrams 
  •  Collaboration diagrams 
  •  Statechart diagrams 
  •  Activity diagrams 
  •  Component diagrams 
  •  Deployment diagrams
In our discussion, we only discuss Use case diagram, Class diagrams, Sequence diagram, Collaboration diagram, Activity diagram.

Use case diagram:

Use case diagram consists of some use cases in your system, some of the actors in your system, and the relationships between them. A use case is a high level piece of functionality for a single task or goal that the system will provide. An actor is who or what initiates the events involved in that task. Actors are simply roles that people or objects play.

Use case diagrams are closely connected to scenarios. A scenario is an example of what happens when someone interacts with the system. Here is a scenario for a medical clinic."A patient calls the clinic to make an appointment for a yearly checkup. The receptionist finds the nearest empty time slot in the appointment book and schedules the appointment for that time slot. "
A use case is a summary of scenarios for a single task or goal. An actor is who or what initiates the events involved in that task. Actors are simply roles that people or objects play. The picture below is a Make Appointment use case for the medical clinic. The actor is a Patient. The connection between actor and use case is a communication association (or communication for short).




Use case diagrams describe what a system does from the standpoint of an external observer. The emphasis is on what a system does rather than how.

It is important to notice that Use Case Diagrams are not suited to represent the design, and cannot describe the internals of a system. Use Case Diagrams are meant to facilitate the communication with the future users of the system, and with the customer, and are especially helpful to determine the required features the system is to have. Use Case Diagrams tell, what the system should do but do not — and cannot specify how this is to be achieved.

A use case diagram is a collection of actors, use cases, and their communications. We've put Make Appointment as part of a diagram with four actors and four use cases. Notice that a single use case can have multiple actors.




Use case diagrams fulfill a specific purpose – to document the actors (everything outside the system scope), the use cases (everything inside the system scope), and their relationships. Some things to keep in mind as you are creating use case diagrams.
  • Don’t model actor to actor communications. By definition, the actors are outside the scope of the current problem domain. The communication between the actors, therefore, is also outside the scope of what you’re building.    
  • Don’t draw an arrow directly between two use cases (except in a uses or extends relationship, which will be described later).
  • Every use cases must be initiated by an actor. Again, the exception here is a uses or extends relationship, which will be described later.



Activity diagrams:

An activity diagram is essentially a fancy flowchart. Activity diagrams and state chart diagrams are related. While a state chart diagram focuses attention on an object undergoing a process (or on a process as an object), an activity diagram focuses on the flow of activities involved in a single process. The activity diagram shows the how those activities depend on one another.For our example, we used the following process.

"Withdraw money from a bank account through an ATM."

The three involved classes (people, etc.) of the activity are Customer, ATM, and Bank. The process begins at the black start circle at the top and ends at the concentric white/black stop circles at the bottom. The activities are rounded rectangles.



Sequence diagrams:

Sequence diagrams are used to show the flow of functionality through a use case. Sequence diagrams are interaction diagrams that are ordered by time; you can read the diagrams from top to bottom. Sequence diagrams are used to show the flow of functionality through a use case. 

For example, the Withdraw Money use case has several possible sequences, such as withdrawing money, attempting to withdraw without available funds, attempting to withdraw with the wrong PIN, and several others. The normal scenario of withdrawing $20 (without any problems such as entering the wrong PIN or insufficient funds in the account)


This Sequence diagram shows the flow of processing through the Withdraw Money use case. Any actors involved are shown at the top of the diagram; the customer actor is shown in the above example. The objects that the system needs in order to perform the Withdraw Money use case are also shown at the top of the diagram. Each arrow represents a message passed between actor and object or object and object to perform the needed functionality. One other note about Sequence diagrams—they display objects, not classes. Classes represent types of objects, Objects are specific; instead of just customer, the Sequence diagram shows Joe. The use case starts with the customer inserting his card into the card reader, an object indicated by the rectangle at the top of the diagram. Then, the card reader reads the card number, opens Joe's account object, and initializes the ATM screen. The screen prompts Joe for his PIN. He enters 1234. The screen verifies the PIN with the account object and they match. The screen presents Joe with his options, and he chooses withdraw. The screen then prompts Joe for the amount to withdraw. He chooses $20. Then, the screen withdraws the funds from the account. This initiates a series of processes that the account object performs. First, Joe's account verifies that the account contains at least $20. Then, it deducts the funds from the account. Next, it instructs the cash dispenser to provide $20 in cash. Joe's account also instructs the dispenser to provide a receipt. Lastly, it instructs the card reader to eject the card. This Sequence diagram illustrated the entire flow of processing for the Withdraw Money use case by showing a specific example of Joe withdrawing $20 from his account. Users can look at these diagrams to see the specifics of their business processing. Analysts see the flow of processing in the Sequence diagrams. Developers see objects that need to be developed and operations for those objects.


Collaboration diagram:

Collaboration diagrams show exactly the same information as the Sequence diagrams. However,
Collaboration diagrams show this information in a different way and with a different purpose. The Sequence diagram is given bellow as a Collaboration diagram:

  
In this Collaboration diagram, the objects are represented as rectangles and the actors are stick figures, as before. Whereas the Sequence diagram illustrates the objects and actor interactions over time, the Collaboration diagram shows the objects and actor interactions without reference to time. For example, in this diagram, we see that the card reader instructs Joe's account to open and Joe's account instructs the card reader to eject the card. Also, objects that directly communicate with each other are shown with lines drawn between them. If the ATM screen and cash dispenser directly communicated with one another, a line would be drawn between them. The absence of a line means that no communication occurs directly between those two objects. Collaboration diagrams, therefore, show the same information as Sequence diagrams, but people look at Collaboration diagrams for different reasons. Quality assurance engineers and system architects look at these to see the distribution of processing between objects. Suppose that the Collaboration diagram was shaped like a star, with several objects communicating with a central object. A system architect may conclude that the system is too dependent on the central object and redesign the objects to distribute the processing power more evenly. This type of interaction would have been difficult to see in a Sequence diagram.


Class diagrams:

A Class diagram gives an overview of a system by showing its classes and the relationships among them. Class diagrams are static -- they display what interacts but not what happens when they do interact.The class diagram below models a customer order from a retail catalog. The central class is the Order. Associated with it is the Customer making the purchase and the Payment. A Payment is one of three kinds: Cash, Check, or Credit. The order contains Order Details (line items), each with its associated Item.



UML class notation is a rectangle divided into three parts: class name, attributes, and operations. Names of abstract classes, such as Payment, are in italics. Relationships between classes are the connecting links.Our class diagram has three kinds of relationships.

  • Association: A relationship between instances of the two classes. There is an association between two classes if an instance of one class must know about the other in order to perform its work. In a diagram, an association is a link connecting two classes.  
  • Aggregation: An association in which one class belongs to a collection. An aggregation has a diamond end pointing to the part containing the whole. In our diagram, Order has a collection of OrderDetails. 
  • Generalization: An inheritance link indicating one class is a superclass of the other. A generalization has a triangle pointing to the superclass. Payment is a superclass of Cash, Check, and Credit.   
An association has two ends. An end may have a role name to clarify the nature of the association. For example, an OrderDetail is a line item of each Order.

A navigability arrow on an association shows which direction the association can be traversed or queried. An OrderDetail can be queried about its Item, but not the other way around.The arrow also lets you know who "owns" the association's implementation; in this case, OrderDetail has an Item. Associations with no navigability arrows are bi-directional.

The multiplicity of an association end is the number of possible instances of the class associated with a single instance of the other end. Multiplicities are single numbers or ranges of numbers. In our example, there can be only one Customer for each Order, but a Customer can have any number of Orders.


This table gives the most common multiplicities.
Multiplicities
Meaning
0..1
zero or one instance. The notation n . . m indicates n to m instances.
0..*  or  *
no limit on the number of instances (including none).
1
exactly one instance
1..*
at least one instance

Every class diagram has classes, associations, and multiplicities. Navigability and roles are optional items placed in a diagram to provide clarity.


Thank you for reading.

Sunday, October 07, 2018

Introducing Object Oriented Programming(OOP) Concept in C#

Introducing Object Oriented Programming(OOP) Concept in C#

OOP Basic Concept






Click to see the Post Contents

Object Oriented Programming What is Object Oriedted Programming?

What is OOP Philosophy?

Class What is class?

Static Member, Namespace  Access Modifier
Static Member, Namespace  Access Modifier?


Inheritance
What is Inheritance?



Encapsulation
What is Encapsulation?


Polymorphism
What is Method Overriding?

What is Up-Casting and Down-Casting?

Interface & Abstract Class
What is Interface?
What is abstract class?

What is the difference between Interface and Abstract Class?







Object-oriented programming (OOP) is a programming language model organized around "objects" rather than "actions" and data rather than logic. Historically, a program has been viewed as a logical procedure that takes input data, processes it, and produces output data. The programming challenge was seen as how to write the logic, not how to define the data. Object-oriented programming takes the view that what we really care about are the objects we want to manipulate rather than the logic required to manipulate them.
Object oriented programming uses objects to design applications. This technique is designed to isolate data. The data and the functions that operate on the data are combined into single unit. This unit is called an object. Each object can have properties and member functions. You can call member function to access data of an object. It is based on several techniques like encapsulation, modularity, polymorphism, and inheritance. 
Basic elements of OOP are:
  • Object
  • Class
  • Method
  • Encapsulation
  • Information Hiding
  • Inheritance
  • Polymorphism
We will describe it step by step.


Friday, October 05, 2018

Test Guidelines: Minimum list of categorized problem that you have to solve

Test Guidelines: Minimum list of categorized problem that you have to solve

In this post I will share my experience about the Job exam question patterns:



In our country, most of the software firms and others are not concerned about the computer programming skill for any job candidates. They are mostly concerned about your expertise of any framework or platform like.net, android, IOS, PHP, etc. But if you want to work with a multinational or world recognized tech giant like Samsung, Google, Apple, Amazon and many more, they must judge you by your computer programming skill. Recently, some Offshore Multinational Software company has been started their business in our country, and they have also included this judgment system in their recruitment system to judge your computer programming and algorithm skill. As we know Software Engineering and Computer Programming is not exactly the same thing. An Expert software engineer may not be a good computer programmer. But good computer programmers who have vast knowledge of the algorithm can be a world-class Software Engineer as he knows how to run a program with low memory resource and in optimized time duration. That’s why Tech giant Companies hire good computer programmer and trained them how to be a good software developer.



Now days there are lots of good online judge system like UVA, Code chef, Code forces, Top coder, Hacker Rank , LightOj, a2oj etc. Among them UVA is one of the best online judge system for all the programmers. They have most categorized problem sets. Today I’ll share a list of problems set by solving those you will get a clear idea about the job exam question pattern. This is not the Bible, but as much as the problem you will solve from this list, your probability of getting passed in job exam will be increased for sure. Let’s start…

Firstly, you have to go the UVA’s Complementary tool's website called uHunt. On this web page there is a text box named UVa username. Type your UVA user name, for example yeasir007 and clicked the view button. You will find user statistics below.




Now scroll down until you found the title name Competitive Programming Exercises. Under this title there is a chart like below image. Click on the 3rd (3rd Edition’s Exercises) then click on Problem Solving Paradigms.








Here you will find a list of problems. Total number of categorized problem is given below:
  • Complete Search – 24 problems 
  •  Dynamic Programming -21 problems 
  •  Divide and Conquer- 7 problems
  • Greedy- 9 problems


Now click on Graph under 3rd (3rd Edition’s Exercises). From this list you have to solve according to the list below:
  • All-Pairs Shortest Paths- 6 problems   
  •  Single-Source Shortest Paths (SSSP)- 15 problems 
  •  Minimum Spanning Tree- 15 problems 
  • Graph Traversal- 18 problems (In this list Flood Fill, Topological Sort and Finding Strongly Connected Components are most important)



Monday, October 01, 2018

The definitions of the SOLID principles in Software Design with examples

The definitions of the SOLID principles in Software Design with examples


SOLID Principles

SOLID principles are the foundation of a good software design which helps developers to write a maintainable and extendable code that can easily adapt with the future requirement. It’s acronym of five principles that are Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion. This acronym was first introduced by Michael Feathers based on Uncle Bob’s (Robert Cecil Martin) paper “Design Principles and Design Patterns”. Here I will explain each of five principles with an example.
 
 



Single Responsibility Principle (Known as SRP)


  1. A class /method should have one and only one reason (purpose or responsibility) to change. That means if anything is changed in the class, it will affect only one particular behavior of the software.
  2. Focus on SOC
    •  Separation of Concern.
    • A method or class should do one thing at a time.
    • Write highly cohesive and loosely coupled code

Example:
Suppose we have to develop a simple calculator which will take input of 2 numbers and calculate the sum as output.
In general approach we do like bellow:


#include
using namespace std;

int main()
{
    double first, second, sum;
    cin >> first >> second;
    sum = first + second;
    cout << sum << endl;
}
Actually we are performing 3 different types of responsibly for this task.
  1. Take input
  2. Process input
  3. Display output

So according to SRP we should write our code like bellow. This approach does not violate the single responsibility principle by abstracting the task in different methods.

#include<iostream>
using namespace std;

class Calculator
{
public:
    double add(double first, double second);
    void display(double sum);
    void takeInput();
};

double g_first, g_second;
int main()
{
   double sum;
   Calculator myCalculator;
   myCalculator.takeInput();
   sum = myCalculator.add(g_first, g_second);
   myCalculator.display(sum);
}

double Calculator::add(double first, double second)
{
   double sum = 0;
   sum = first + second;
   return sum;
}

void Calculator::display(double sum)
{
  cout << sum << endl;
}

void Calculator::takeInput()
{
   g_first = g_second = 0;
   cin >> g_first >> g_second;
}



Open/Closed Principles


  1. Software entities (classes, modules, functions etc.) should be open for extension but close for modification. (Here modification defines by the developer’s response to the client’s changed requirement.)  Suppose a class CAR is developed by AAA and developer BBB wants some modification on BREAK method of this class. In that case developer BBB can do this easily by extending/inheriting this class not by modifying this class.
  2. Class behavior should be changed by inheritance and composition.

Example:
Let say we have to implement a class which will calculate the salary of an office and there are three types of employees:
  • Manager (Salary: 100000/=)
  • Officer (Salary: 50000/=)
  • Clerk (Salary: 20000/=)

In general code will be like bellow:
#include

using namespace std;

enum EmployeeTypes
{
   Manager,
   Officer,
   Clerk
};

class SalaryCalculator
{
public:
   double getSalary(EmployeeTypes type);
};

double SalaryCalculator::getSalary(EmployeeTypes types)
{
  switch (types)
  {
    case Manager:
        return 100000;
    case Officer:
        return 50000;
    case Clerk:
        return 20000;
    default:
        return 0;
  }
}


Now Clients changed the requirement:

  1. Put taxes for the employees who withdraw salary more than 50000/=
  2. Want to introduce Human Resource Manager into office who will get 35000/= per month.

What can we do for that change?

  1. Add another enum type
  2. Add another check in the switch
  3. Add logic to check whether the salary is more than 50000/= or not etc..

What will happen if we frequently changed our class?
  1. This may cost in functionality malfunctioning.
  2. Class is getting bigger and various type of code smell will arises.
  3. Maintenance of the code will be getting harder followed by requirement changes.
  4. Unit test case may fail for those random changes.

So what can we do?
Salary calculator is calculating salary for each type of employee which is not good. Salary calculator is going to be changed for multiple purposes like:
  • Tax rule
  • Introduction of new employee
  • Change of salary amount
  • Change of salary calculation logic

Let’s apply SRP here:
  • Decouple the responsibility of this class
  • Introduce inheritance and interface if needed
  • Use dynamic polymorphism etc..
  • Use abstraction

Solution Steps:
  1. Now declare an abstract class for Employee
class Empolyee
{
 public:
   virtual double getSalary();
};
     2. Create subclass by inheriting Employee class for different types of employee. 
class Officer : Empolyee
{
 public:
   double getSalary()
   {
      return 50000;
   }
};

class Manager : Empolyee
{
 public :
    double getSalary()
    {
       return 100000;
    }
};

class Clerk : Empolyee
{
 public:
  double getSalary()
  {
    return 20000;
  }
};

     3. Now Salary calculator class will be changed like bellow:

class SalaryCalculator
{
 public:
   double getSalary(Empolyee type);
};

double SalaryCalculator::getSalary(Empolyee type)
{
  return type.getSalary();
}

Now we can calculate the salary for different types of employees like bellow:

void display(double salary)
{
  cout << salary << endl;
}

int main()
{
  SalaryCalculator mySalaryCalculator;
  Manager myManager;
  Officer myOfficer;
  Clerk myClerk;

  display ( mySalaryCalculator.getSalary( myManager ) );
  display ( mySalaryCalculator.getSalary( myOfficer ) );
  display ( mySalaryCalculator.getSalary( myClerk ) );
}
Now we can deal with customer changed requirement easily.
  • Put 1% tax is applied for the salary more than 50000/=.

Take a look, no other class/subclasses will be affected for this changed requirement. Only getSalary () method of the SalaryCalculator class will be changed little bit like bellow.
 

class SalaryCalculator
{
 public:
   double getSalary(Empolyee type);
};

double SalaryCalculator::getSalary(Empolyee type)
{
   double salary = type.getSalary();
   if (salary > 50000) //Applied Tax deduction rule according to change requirement
   {
      double tax = salary * 0.1;
      return salary - tax;
   }

   return salary;
}
  • Introduced Human Resource Manager having salary 35000/=

We can easily add another subclass for these new Types of resources by inheriting Employee class. We don’t have to change our existing code for this changed requirement.
code here
 
class HRM : public Empolyee
{
 public:
   double getSalary()
   {
      return 35000;
   }
};


Liskov's Substitution Principles


  1. The parent class should be able to refer child class object seamlessly during runtime polymorphism. Assume we have a class Tesla which is a subclass of class Car. According to LSP an object of class Car will be replaceable by the object of class Tesla without changing the behavior of the program.
  2. The implementation of this principle can be a little bit tricky if we combine it with Open-Closed Principle. Due to extend the behavior of subclass we have to make sure that we can still exchange the base class with the derived class without breaking anything.
Example:
Let’s say we want to show the behavior of animals:
  1. The specific animal should apply the behavior according to them.
  2. Animal class might have the following behaves:
    •  Eat
    • Drink
    • Run
    • Fly

So we can implement the Animal class like bellow:
class Animal
{
public:
  virtual void Eat();
  virtual void Drink();
  virtual void Run();
  virtual void Fly();
};

Now we want to introduce Lion’s behavior: 
  1. Lion is an animal, so it will inherit Animal class.
  2. So it should be:
  • Eat
  • Drink
  • Run
 
But does it Fly?     
So we cannot consider Lion’s object as Animal’s object according to Liskov’s Substitution Principle.

So what can we do?
  •   According to GOF (Gang of Four): Favor object composition over class inheritance.
  •   We should figure out the common behavior for all of the Animals and put those in Animal class.
     
For specific functionalities we should use Interface instead of putting those in base class like bellow.

class Animal
{
 public:
   void Eat();
   void Drink();
};

void Animal::Eat()
{
  cout << "Can eat" << endl;
}

void Animal::Drink()
{
  cout << "Can drink" << endl;
}

 __interface IRunnable
{
 public:
   void Run();
};

 __interface IFlyable
{
 public:
    void Fly();
};

Now we can implement Lion’s class:

class Lion : public Animal, public IRunnable
{
 public:
   void Eat();
   void Drink();
   void Run();
};

void Lion::Eat()
{
  cout << "Eat flesh" << endl;
}

void Lion::Drink()
{
  cout << "Drink water" << endl;
}

void Lion::Run()
{
  cout << "Runs fast" << endl;
}


Now we can introduce Eagle class as Animal easily:

class Eagle : public Animal, public IFlyable
{
 public:
   void Eat();
   void Drink();
   void Fly();
};

void Eagle::Eat()
{
  cout << "Eat flesh" << endl;
}

void Eagle::Drink()
{
  cout << "Drinks water" << endl;
}

void Eagle::Fly()
{
  cout << "Fly high" << endl;
}

Now, wherever in our code we were using Animal class object we must be able to replace it with the Lion or Eagle without exploding our code. What do we mean here is the child class should not implement code such that if it is replaced by the parent class then the application will stop running.


Interface Segregation Principles


  1. A client should not be forced to use an interface, if it doesn’t need it.
  2. Make fine grained interfaces that are client specific, small and cohesive.

Classes should be as specialized as possible. We shouldn’t develop any god classes that contain the whole application logic. The source code should be modular and every class should contain only the minimum necessary logic to achieve the desired behavior. The same goes for interfaces. Make small and specific interfaces so the client who implements them does not depend on methods it does not need. Instead of one class that can handle three special cases it is better to have three classes, one for each special case.

Let’s say, we have to implement a Smart device that can perform some operation like Print, Scan and Fax. So interface can be like bellow:

__interface ISmartDevice
{
  void Print();
  void Fax();
  void Scan();
};

This interface states that a smart device is able to print, fax, and scan. If we want to implement a smart device class AllInOnePrinter which can perform all of this action then it will be like bellow:

class AllInOnePrinter : public ISmartDevice
{
 public:
   void Print();    
   void Fax();
   void Scan();
};

void AllInOnePrinter::Print()
{
   cout << "Printing code." << endl;
}

void AllInOnePrinter::Fax()
{
  cout << "Beep booop biiiiip." << endl;
}

void AllInOnePrinter::Scan()
{
  cout << "Scanning code." << endl;
}

Now suppose we need to implement another smart device class named EconomicPrinter which can only perform Print operation. In that case we have to force to implement the whole interface, which is not a good practice at all.

class EconomicPrinter : public ISmartDevice
{
 public:
   void Print();
   void Fax();
   void Scan();
};

void EconomicPrinter::Print() 
{
  cout << "Yes I can print." << endl;
}

void EconomicPrinter::Fax()
{
  throw new exception;
}

void EconomicPrinter::Scan()
{
  throw new exception();
}

So what can we do?
Here we can apply the ISP by separating the single ISmartDevice interface into three smaller interfaces: IPrinter, IFax, and IScanner.


__interface IPrinter 
{
   void Print();
};

__interface IFax 
{
   void Fax();
};

__interface IScanner 
{
   void Scan();
};


Now our code is more decoupled and easier to maintain. Let's re-implement our EconomicPrinter class with this architecture:

class EconomicPrinter :public IPrinter
{
 public:
   void Print();
};

void EconomicPrinter::Print() 
{
   cout << "Yes I can print." << endl;
}

In that case our AllInOnePrinter would be like bellow:

class AllInOnePrinter :public IPrinter,public IFax,public IScanner
{
 public:
   void Print();    
   void Fax();
   void Scan();
};

void AllInOnePrinter::Print()
{
  cout << "Printing code." << endl;
}

void AllInOnePrinter::Fax()
{
  cout << "Beep booop biiiiip." << endl;
}

void AllInOnePrinter::Scan()
{
   cout << "Scanning code." << endl;
}



Dependency Inversion Principles




  1. High level modules should not depend upon low level modules. Both should depend upon abstractions by using a technique like inheritance or interface.
  2. Abstraction should not depend on details. Details should depend on abstraction.

Example:
Let’s say, we have two classes named Manager and Worker where Manager is a higher level class and Worker is a lower level class.
  • Manager has two types of task like SetWorker and Manage
  • Worker  has one simple task like DoWork


class Worker
{
 public:
   void doWork();
};

void Worker::doWork()
{
  cout << "working..." << endl;
}


class Manager
{
 public:
   Worker worker;

   void setWork(Worker w);
   void manageWorker();     
};

void Manager::setWork(Worker worker)
{
   this->worker = worker;
}

void Manager::manageWorker()
{
   worker.doWork();
}

Now there is a requirement change, we have to introduce other types of employee called SuperWorker. What we have to do if we want to introduce this new SuperWorker?
  • We have to change the Manager class
  • Some of the current functionality from the Manager class might be affected.
  • Unit test case may fail or should be redone.




class SuperWorker
{
 public:
   void doWork();
};

void SuperWorker::doWork()
{
   cout << "Working much more" << endl;
}


class Manager
{
public:
  Worker worker;
  SuperWorker superWorker;

  void setWork(Worker w);
  void setWork(SuperWorker w);

  void manageWorker();
  void manageSupperWorker();        
};

void Manager::setWork(Worker worker)
{
   this->worker = worker;
}
void Manager::setWork(SuperWorker superWorker)
{
   this->superWorker = superWorker;
}

void Manager::manageWorker()
{
   worker.doWork();
}
void Manager::manageSupperWorker()
{
   superWorker.doWork();
}

Okay, take a break.
Let’s assume the Manager class is quit complex, containing very complex logic and lots of activities like WorkingTimeCalculation, DailyBasisSalaryGeneration etc..
  • Just think, if most the function of this class is dependent on Worker class, what will happened?
  • All those problems could take a lot of time to solve and might introduce new errors in the old perfect functionality.
  • What will happened if another type of Worker introduce again?

So what can we do?
We can solve this kind of problem by applying DIP( Dependency Inversion Principle ).

  • Now we will add a new abstraction layer through IWorker Interface followed by DIP.
         

__interface IWorker
{
 public:
   void doWork();
};
  •  Lower classes will implement this Interface.
class Worker: public IWorker
{
 public:
   void doWork();
};

void Worker::doWork()
{
   cout << "working..." << endl;
}

class SuperWorker: public IWorker
{
 public:
    void doWork();
};

void SuperWorker::doWork()
{
   cout << "Working much more" << endl;
}

  •        Supper class like Manager will work with IWorker. By this way we can also introduce different types of Worker without changing the Manager class.
class Manager
{
 public:
   IWorker *worker;

   void setWork(IWorker *worker);
   void manageWorker();
};

void Manager::setWork(IWorker *worker)
{
   this->worker = worker;
}

void Manager::manageWorker()
{
   worker->doWork();
}
So, if we applied DIP high level class will not work with low level class directly rather than it will use an abstract layer by using Interface.

Conclusions:
The SOLID principles are guidelines that can help to create maintainable and extendable code. As Uncle Bob says: “They are not laws. They are not perfect truths”. Nevertheless, this guideline will help to create clean code. Finally as a developer you should at least know these principles by which you can take decision where to apply them or not.

Thanks for your endurance.