1. 程式人生 > >軟體設計Solid原則學習總結

軟體設計Solid原則學習總結

軟體質量度量指標有正確性、可維護性、效率性等;其中正確性可以通過TDD來保證,可維護性可以通過規範程式碼、重構程式碼來提升,效率性需要平時或者後期對軟體的效能進行優化以達到所要的指標...

為了能讓專案組開發團隊都寫出CleanCode,需要在專案組制定相應的規範(包括命名規範、程式碼編寫規範、註釋規範、開發流程規範、文件規範),並持續應用宣貫。規範是指專案成員都需要遵循一定的規律和原則來開發軟體,使得專案成員之間能夠無障礙地閱讀、理解和處理物件。保證統一正確的風格,團隊成員可以更好的溝通交流。所以每一位開發成員在正式進行開發之前,都需閱讀和嚴格遵循相關規範。

保證Clean Code需要注意的事項很多,平時編碼注意程式碼中的壞味道,利用重構的思想來處理這些壞味道。比如使用抽取方法,抽取類,替換演算法,使用狀態和策略等設計模式替換一大堆的if語句等。在平時多多積累總結,才能寫出高質量的程式碼。

重構與設計模式有著密切的聯絡,而進行重構和設計模式的很多思想都是基於Solid原則的,所以理解Solid原則顯得尤為重要。

單一職責原則 Single Responsibility Principle SRP
對於一個類而言,引起他變化的原因應該只有一個,如果有多個,那麼應該把職責分離,讓一個類只做一件單一的事情,因為一個類做的事情越多,改動的概率越大;改動的概率越大,出錯的概率就越大。我覺得這也是最能體現高內聚低耦合思想的原則之一。

反例:該類有不屬於執行緒服務的QueryLog方法
public interface IThreadService{
	public List<ThreadVO> QueryThread(String threadId);
	public boolean AddThread(ThreadVO thread) throws Exception;		
	public List<LogVO> QueryLog(String objectId) throws Exception;
}
正例:拆分成兩個類,降低介面間的耦合性
public interface IThreadService  extends IBasicService<ThreadVO>{
	public List<ThreadVO> QueryThread(String threadId);
	public boolean AddThread(ThreadVO thread) throws Exception;	
}
public interface ILogService{
public List<LogVO> QueryLog(String objectId) throws Exception;
}


開放封閉原則 Open-Close Principle OCP
開放封閉原則指對於擴充套件是開發的,對於修改的封閉的。在設計過程中,儘量保證我們的類或者模組不被修改的前提下可以進行擴充套件,通過新增類或模組的方式來新增功能。這是OOD的基石。



反例:對於修改siebel或smartsales的監控功能,都需要修改該方法
public class SystemMonitor{
	public void Monitor(String systemName){
		if(systemName.equalsIgnoreCase("siebel")){
			...
		}
		else if(systemName.equalsIgnoreCase("smartsales")){
			...
		}
	}
}

正例:新增監控抽象類,如果需要實現監控功能就繼承該類,對於siebel和smartsales的監控功能都放在各自的類裡,如果需要修改只需要修改各自的類。
如果要新增MSM的監控,只需要增加一個MonitorMSM的類。這樣對於擴充套件就是開放的了。

public class SystemMonitor{
	Monitor monitor;
	public SystemMonitor (Monitor monitor){
		this.monitor = monitor;
	}
	
	public void Monitor(){
		monitor.execute();
	}
}

public abstract class Monitor {
	public abstract void execute(String url);
}

public class MonitorSmartSalesTomcat extends Monitor{
	@Override
	public void execute(String url) {
		...
	}
}

public class MonitorSiebelTomcat extends Monitor{
	@Override
	public void execute(String url) {
		...
	}
}


里氏替換原則 Liskov Subtitution Principle LSP
對於能使用父類的地方,一定可以使用它的子類代替,替換後不能察覺出父類與子類的差別。對於正方形長方形、圓形橢圓形這樣的例子不滿足里氏替換原則。
里氏代換原則是實現開閉原則的重要方式之一,由於使用基類物件的地方都可以使用子類物件,因此在程式中儘量使用基類型別來對物件進行定義,而在執行時再確定其子類型別,用子類物件來替換父類物件。
反例:SystemMonitor 的MonitorSmartSalesTomcat和MonitorSiebelTomcat方法傳遞的引數都是子類,根據里氏替換原則,我們應該在定義時使用積累,在執行時在確定其子類型別。
public class SystemMonitor{
	public void MonitorSmartSalesTomcat(MonitorSmartSalesTomcat system){
		system.execute();
	}
	public void MonitorSiebelTomcat(MonitorSiebelTomcat system){
		system.execute();
	}
}

public abstract class Monitor {
	public abstract void execute(String url);
}

public class MonitorSmartSalesTomcat{
	@Override
	public void execute(String url) {
		...
	}
}

public class MonitorSiebelTomcat extends{
	@Override
	public void execute(String url) {
		...
	}
}






正例:

public class SystemMonitor{
	public void Monitor(Monitor system){
		system.execute();
	}
}

public abstract class Monitor {
	public abstract void execute(String url);
}

public class MonitorSmartSalesTomcat extends IMonitor{
	@Override
	public void execute(String url) {
		...
	}
}

public class MonitorSiebelTomcat extends IMonitor{
	@Override
	public void execute(String url) {
		...
	}
}


介面隔離原則	Interface Segregation Principle ISP
使用多個專門的介面比使用一個總介面要好,應該根據客戶的需要提供不同的介面,不應該讓客戶依賴與他們不需要的方法。否則會產生不正常而且有害的耦合關係,如果一個客戶程式要求改動介面,會影響到其他的客戶程式。

反例:提供的執行緒介面有客戶不需要使用的QueryLog方法
public interface IThreadService{
	public List<ThreadVO> QueryThread(String threadId);
	public boolean AddThread(ThreadVO thread) throws Exception;		
	public List<LogVO> QueryLog(String objectId) throws Exception;
}
正例:拆分成兩個介面,降低介面間的耦合性
public interface IThreadService  extends IBasicService<ThreadVO>{
	public List<ThreadVO> QueryThread(String threadId);
	public boolean AddThread(ThreadVO thread) throws Exception;	
}
public interface ILogService{
public List<LogVO> QueryLog(String objectId) throws Exception;
}


依賴倒轉原則 Dependence Inversion Principle DIP
依賴倒轉有以下幾點表述方式
1.	高層模組不應該依賴於低層模組。二者都應該依賴於抽象。
2.	抽象不應該依賴於細節。細節應該依賴抽象。
3.	要針對介面程式設計,而不是針對實現程式設計。
4.	傳遞引數,儘量引用層次高的類。
反例:SystemMonitor依賴於兩個具體的監控類
public class SystemMonitor{
	public void execute(String systemName){
		if(systemName.equalsIgnoreCase("siebel")){
			MonitorSiebelTomcat monitor = new MonitorSiebelTomcat();
			monitor.execute();
		}
		else if(systemName.equalsIgnoreCase("smartsales")){
			MonitorSmartSalesTomcat monitor = new MonitorSmartSalesTomcat();
			monitor.execute();
		}
	}
}

public class MonitorSmartSalesTomcat{
	@Override
	public void execute(String url) {
		...
	}
}

public class MonitorSiebelTomcat extends{
	@Override
	public void execute(String url) {
		...
	}
}

正例:新增監控抽象類,SystemMonitor依賴於抽象,而不依賴於具體的實現


public class SystemMonitor{
	IMonitor monitor;
	public SystemMonitor(IMonitor monitor){
		this.monitor = monitor;
	}
	
	public void Monitor(){
		monitor.execute();
	}
}

public abstract class Monitor {
	public abstract void execute(String url);
}

public class MonitorSmartSalesTomcat extends IMonitor{
	@Override
	public void execute(String url) {
		...
	}
}

public class MonitorSiebelTomcat extends IMonitor{
	@Override
	public void execute(String url) {
		...
	}
} 

2016.07.21