Skip to content

Latest commit

 

History

History
384 lines (258 loc) · 6.49 KB

solid.org

File metadata and controls

384 lines (258 loc) · 6.49 KB

Software Design

S.O.L.I.D.

Class Design (5)

SRP - The Single Responsibility Principle

The class should have one, and only one, reason to change. A class have hight cohesion if it’s behaviours are coselly related.

class Integration {
private:
	bool PaseXML(Document doc);	
public:	
	bool ImportData(Document doc);
		
};


bool Integration::ImportData(Document doc){
	bool result = ParseXML(doc); // (2)
	ShowGUIDialogBox(result);    // (1)
	return result;
}

/*

Problems:

(1) - This class has two function: parse a XML file and present on GUI the result
(2) - The paser works for XML files, and it we want to parse a JSON
file this will lead to a major refactor

Possible Solution:

(1) declouple classes by responsability adding a new levels os indirection

*/


class Integration {
private:
	bool PaseXML(Document doc);	
	IParser* _parser;
	GUI* _gui;
public:	
	bool ImportData(Document doc);
		
};


bool Integration::ImportData(Document doc){
	bool result =  _parser->importXMlFile(doc); //(1)
	_gui->printMessage(result);                 //(1)
	return result; 
}

OCP - The Open Closed Principle

You should be able to extend a class behaviour, without modifying it

enum FileType
{
	XML, 
	JSON
};

class Integration {
public:
	void setFileType(FileType ftype);
	void Import();
private:
	FileType _fileType;
};


void Integration:setFileType(FileType ftype){
	_fileType = ftype;
}

void Integration::Import() {
	switch(_ftype)	{   //(1)
		case XML:
			ParseXML(...);
			break;	
		case JSON:
			ParseJSON(...);
			break;
		\\...
		
	}
}

/*

Problems:

Integration class is not closed to modification. Every time we need to import a 
new file type, we have to add a new enums, add new parser ...


Possible solution:

Use inheritance and polymorphism to create multiple parsers with specific
implementations acording the file type to import

*/

class IParser {
public:
	virtual void Import(...) = 0;
};

class XMLParser: public IParser {
public:
	XMLParser() = default;
	virtual void Import(...) override;	 		
};

class JSONParser: public IParser {
public:
	JSONParser() = default;
	virtual void Import(...) final;	 		
};

class Integration {
public:
	Integration(shared_prt<IParser> parser)
	void setFileType(FileType ftype);
	void Import();
private:
	shared_prt<IParser> _parser;
};

void Integration::Import() {
	parser->Import(...);
}	

// How to use it

Integration integration(make_shared<XMLparser>());

// or

Integration integration(make_shared<JSONparser>());

LSP - The Liskov Substitution

Derived classes must be substitutable for their base classes. A nice example using C++ would be using pointers, functions that use pointers from base class can use objects from derived classes without knowing it.

class Bird {
public:
    virtual void setLocation(double longitude, double latitude) = 0;
    virtual void setAltitude(double altitude) = 0;
};

class Penguin: public Bird {
    virtual void setLocation(double longitude, double latitude) override;
    virtual void setAltitude(double altitude) override;
};

virtual Penguin::setAltitude(double altitude)
{
    //altitude can't be set because penguins can't fly
    //this function does nothing
}

/*

Problems:

Penguins can't fly so it's  an Object Oriented trap !!

Solution:
 
In C++ we can use multiple inheritance

*/


class Bird {
public:
    virtual void draw() = 0;
    virtual void setLocation(double longitude, double latitude) = 0;
};

class FlightfulBird : public Bird {
public:
    virtual void setAltitude(double altitude) = 0;
};


class Penguin: public Bird {
	// ...
};

class Eagle: public Bird, public FlightfulBird {
	// ...
}

source code based on: SOLID Class Design: The Liskov Substitution Principle

ISP - The Interface Segregation Principle

Make fine grained interfaces that are client specific. Clients should not be forced to depend on ippon interfaces that they not use.

class ISmartDevice {
	virtual void Print() = 0;
	virtual void Fax() = 0;
	virtual void Scan() = 0;
};

class MultiPrinterDevice(): ISmartDevice {

	virtual void Print() override {
		// do something ...	
	}

	virtual void Fax() override {
		// do something ...	
	}

	virtual void Scan() override {
		// do something ...	
	}
};


class EconomicPrinterDevice(): ISmartDevice {

	virtual void Print() {
		// do something ...	
	}

	virtual void Fax() {         // (1)
		// do nothing ...	
	}

	virtual void Scan() {
		// do something ...	
	}
};


/*

Problem:

(1) If we inherit from ISmartDevice we have to override all methods even it
we don't use it. What leads to empty methods, bad returns code, ....

Solution:

Had new levels of indirections, e.g using multiple inheritance

*/

class IPrinterDevice {
	virtual void Print() = 0;
};

class IFaxDevice {
	virtual void Fax() = 0;
};

class IScannertDevice {
	virtual void Scan() = 0;
};

class MultiPrinterDevice: public IPrinterDevice, public IFaxDevice, public IScannerDevice{

//...

};

class EconomicPrinterDevice: public IPrinterDevice, public IScannerDevice{

//...

};

DIP - The Dependency Inversion Principle

Depend on abstractions, not on concretions. High modules should not depend upon low levels

class Button{
private:
	unique_ptr<Light> _light;	
public:
	void PressOn(){
		_light.TurnOn();  //(1)
	}
};

/*

Problem:

(1) - Class button should not depend on upper class Light

Solution:

Add a middle man, and Interface that can had a new level of indirection		

*/

class IDevice {
public:
	virtual void TurnOn() = 0;
	virtual void TurnOff() = 0;
};

class Light: public IDevice {
public:
	virtual void TurnOn() {
		// Do Something ...
	}
	
	virtual void TurnOff() {
		// Do Something ... 
	}
};

class Button {
private:
	unique_ptr<Device> _device; 
public:
	void PressOn() {
		_device.TurnOn();
	}
};

Pack Cohesion (3)

REP- The Release Reuse Equivalency

The granule of reuse is the granule of release

CCP - The Common Closure Principle

Classes that change together are packed together

CRP - The Common Reuse Principle

Classes that are used together are packed together

Couple Between Packaged (3)

ADP - The Acyclic Dependencies Principle

The dependency graph of packages must have no cycles

SDP - The Stable Dependencies Principle

Depend in direction of stability

SAP - The Stable Abstractions Principle

Abstractness increases with stability