設計模式之禪-思考上

6大基本原則

程序是面向現實的抽象。

單一職責:類的功能儘量單一,職責單一,只幹一類事情。應該有且僅有一個原因引起類的變更。這個要根據實際項目來設計 類的職責。儘量做到單一。
裏式替換 :所有引用基類的地方必須能透明的使用其子類的對象。就是說 有可能變化的地方,和耦合的地方 儘量使用 接口或者抽象父類 聲明參數。這樣後期容易修改。
依賴倒置 :高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象。抽象不應該依賴細節,細節應該依賴抽象。高層模塊和低層模塊容易理解,每一個邏輯的實現都是由原子邏輯組成的,不可分割的原子邏輯就是低層模塊,原子邏輯的再組裝就是高層模塊。就是說 類與類之間的依賴關係(有6大關係1.繼承2實現3.關聯4依賴5聚合6組合) 儘量避免直接依賴,而應該依賴其接口或者父類。依賴注入的3種寫法:1.構造函數傳遞依賴對象。2.Set方法中傳遞依賴對象。3.接口聲明依賴對象。
接口隔離 :類間的依賴關係應該建立在最小的接口上。這個我也理解都不是特別清楚,只能說設計接口的時候 不要搞一個很大的接口,然後大家都來實現,結果很多都是空實現。應該根據需要,單一等因素設計 合適大小的接口、
迪米特原則 :也叫最小知道原則。一個對象應該對其他對象有最少了解。或者說 只與直接的朋友通信。朋友類的定義是:出現在成員變量,方法的輸入輸出參數中的其他類稱爲該類的朋友類。另一方面也是一個類儘量不要對外公佈太多的public方法和非靜態的public變量。儘量內斂,多使用private package-private protected等訪問權限。如果一個方法放到本類總,既不增加類間關係,也對本類不產生負面影響,那就放置在本類中。迪米特法則的核心管理就是類間解耦,弱耦合。
開閉原則 :最重要的一個原則。一個軟件實體如類,模塊和函數應該對擴展開放,對修改關閉。怎麼樣才能做到以上 那就要上面的5個原則都是爲此服務。1.抽象約束2.元數據控制模塊行爲3.封裝變化。測試:1.正常測試2邊界測試3異常測試

23種設計模式

單例模式:使此類在程序運行只有一個對象。
構造函數設置爲private,原理:靜態修改的變量叫做類變量,簡單的說就是這個變量是和類在一起的。不管你new多少個這個類的對象,這個類變量只有一份。它存儲在內存中的方法區。創建單例模式有很多種方式,我最喜歡用的是 靜態內部類實現單例如:

	//提供給外部的方法
	public static Data getInstance() {
		return DataHolder.sInstance;
	}
	//靜態內部類
	public static class DataHolder {
		public static Data sInstance = new ConnectSerial5();
	}
	//私有構造函數
	private Data () {
	}

這個方式的好處是保證單例的唯一,對象的創建和類的初始化一起,根據類加載原則,類只會加載一次。類圖比較簡單:
在這裏插入圖片描述

工廠方法:定義一個用於創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。
一般我們創建對象都是直接new。但是這樣會有一個不好,就是當需要創建的類的構造函數發生改變,那麼高層用戶也需要改變。這樣不利解耦,所以在中間加一層,專門管理這個類的對象創建。如:

public static void main(String[] args) {
		//通常創建對象
		AAA aaa=new AAA();
		//使用簡單工廠創建對象
		AAA bbb=SingleFactory.getInstance();
	}
public class SingleFactory {
	public static AAA getInstance(){
		return new AAA();
	}
}

當然這只是最簡單的寫法。還有很多變形的寫法,但是核心就是增加了一個factory類管理對象的創建。不過也不要什麼對象都通過工廠創建,我認爲只有一組很類似的對象創建才需要,如,創建香蕉,蘋果,橘子 等。就可以創建一個水果工廠管理創建。其通用類圖如下:
在這裏插入圖片描述

抽象工廠:爲一組相關或相互依賴的對象提供一個接口,而且無需指定它們的具體類。
抽象工廠的UML類圖和工廠方法是一樣的。以下是我對這2個模式的理解:
根據定義抽象工廠是爲一組相關或者相互依賴的對象提供接口,而工廠方法並沒這麼說,只是說爲一個對象的創建提供接口,就是說抽象工廠是有限制條件的,相關或者相互依賴。例如上面工廠方法中的水果,蘋果和香蕉是沒什麼相關性的,只是同屬水果,但是這個太廣泛,太大了。
那哪些對象可以說是有相關的,海南香蕉和越南香蕉就是一組相關的對象。都是香蕉,只不過是按地域分的,無籽香蕉和有籽香蕉也是相關的。
那哪些對象可以說是有相互依賴的,車的左門和右門。男人和女人。
總結:總的來說 工廠方法和抽象工廠 功能都是一樣的,增加一箇中間層factory管理對象的創建。不同的是 對創建對象的分類定義上有所以不同,簡單工廠無限制,抽象工廠是針對一組相關或者相互依賴對象提供接。所以這些差別就反應在工廠接口中定義的方法:
舉個栗子:以水果 蘋果 香蕉爲例:
工廠方法:

//水果的抽象類
public abstract class abstractFruit {
	abstract void eat();
}
public class Apple extends abstractFruit {
	void eat() {
		System.out.println("我是蘋果");
	}

}
public class Banana extends abstractFruit {
	void eat() {
		System.out.println("我是香蕉");
	}
}
//簡單工廠抽象類
public interface ISingleFactory {
	abstractFruit  createFruit(String clsName);
}
簡單工廠實現類-這裏爲了簡單採用反射創建對象
public class SingleFactory implements ISingleFactory {
	@Override
	public abstractFruit createFruit(String clsName) {
		abstractFruit fruit = null;
		try {
			fruit = (abstractFruit) Class.forName(clsName).newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return fruit;
	}
}
public static void main(String[] args) {
		ISingleFactory singleFactory = new SingleFactory();
		abstractFruit apple = singleFactory.createFruit("com.zx.test.factory.Apple");
		apple.eat();
		abstractFruit banana = singleFactory.createFruit("com.zx.test.factory.Banana");
		banana.eat();
	}

運行結果:
在這裏插入圖片描述
抽象工廠:

public class AppleBlue extends abstractFruit {
	void eat() {
		System.out.println("我是藍色蘋果");
	}

}
public class AppleRed extends abstractFruit {
	void eat() {
		System.out.println("我是紅色蘋果");
	}

}
public class BananaBlue extends abstractFruit {
	void eat() {
		System.out.println("我是藍色香蕉");
	}
}

public class BananaRed extends abstractFruit {
	void eat() {
		System.out.println("我是紅色香蕉");
	}
}
//工廠抽象類
public interface IAbstractFactory {
	
	abstractFruit  createApple();
	
	abstractFruit  createBanana();
}
//生產紅色水果工廠
public class RedAbstractFactory implements IAbstractFactory {

	@Override
	public abstractFruit createApple() {
		return new AppleRed();
	}
	@Override
	public abstractFruit createBanana() {
		return new BananaRed();
	}
	
}
//生產藍色水果工廠
public class BlueAbstractFactory implements IAbstractFactory {

	@Override
	public abstractFruit createApple() {
		return new AppleBlue();
	}
	@Override
	public abstractFruit createBanana() {
		return new BananaBlue();
	}
	
}
//測試
public static void main(String[] args) {
		//工廠方法
		ISingleFactory singleFactory = new SingleFactory();
		abstractFruit apple = singleFactory.createFruit("com.zx.test.factory.Apple");
		apple.eat();
		abstractFruit banana = singleFactory.createFruit("com.zx.test.factory.Banana");
		banana.eat();
		System.out.println("-----------------------------------");
		//抽象工廠
		IAbstractFactory abstractFactoryRed=new RedAbstractFactory();
		abstractFruit appleRed = abstractFactoryRed.createApple();
		appleRed.eat();
		abstractFruit bananaRed = abstractFactoryRed.createBanana();
		bananaRed.eat();
		IAbstractFactory abstractFactoryBlue=new BlueAbstractFactory();
		abstractFruit appleBlue = abstractFactoryBlue.createApple();
		appleBlue.eat();
		abstractFruit bananaBlue = abstractFactoryBlue.createBanana();
		bananaBlue.eat();
	}

結果:
在這裏插入圖片描述
總結:通過這個2個例子可以看出,抽象工廠和工廠方法的區別,是需求不同導致工廠接口的方法不同。所以 要根據你具體的需求來確定。比如上面的水果,你需求是要香蕉,蘋果,橘子,西瓜等等 那就用工廠方法。如果你需求是 要 藍色香蕉,藍牙蘋果,藍色橘子。紅色香蕉,紅色蘋果,紅色橘子 等 是一組相關的產品那就用抽象工廠。

模板方法:定義一個操作中的算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個算法的結構即可重定義算法的某些特定步驟。
爲了防止惡意的操作,一般模板方法都加上final關鍵字,不允許被覆寫。
模板方法比較簡單 以一個人個的一天爲例:

// 定義一個父類,人一天的生活
public abstract class AbstractPeople {
	void rise() {
		System.out.println("起牀");
	}

	void eat() {
		System.out.println("喫飯");
	}

	abstract void work();

	void sleep() {
		System.out.println("睡覺");
	}

	// 打印一天
	public final void onDay() {
		rise();
		eat();
		work();
		sleep();
	}
}
public class AdultPeople extends AbstractPeople {

	@Override
	void work() {
		System.out.println("賺錢");
	}
}
public class StudentPeople extends AbstractPeople {

	@Override
	void work() {
		System.out.println("讀書");//學生的工作就是讀書
	}
}
public static void main(String[] args) {
		AbstractPeople adult=new AdultPeople();
		adult.onDay();
		System.out.println("---------------------------");
		AbstractPeople student=new StudentPeople();
		student.onDay();
	}

運行結果:
在這裏插入圖片描述
通用的類圖:
在這裏插入圖片描述
這裏還有一個常用的技巧就是設置一個鉤子。如正常人每天都需要賺錢,但是富二代不用啊,即可以改成如下:
在這裏插入圖片描述
在這裏插入圖片描述

建造者 :將一個複雜對象的構建與它的表示分離,使得構建過程可以創建不同的表示。
這個和工廠模式很像,都是添加中間層去管理對象的創建,但是建造者和工廠 的側重點不同,工廠側重整體創建,建造者側重 部分,就是創建的過程,流程和 拼裝的部分。還是以上面創建水果對象爲例,不知道對不對,:

public interface Apple {
	void eat();
	void setColor(String color);
}
public class ConcreteApple implements Apple {

	private String color = "紅色";

	@Override
	public void setColor(String color) {
		this.color = color;
	}

	@Override
	public void eat() {
		System.out.println("我是" + color + "蘋果");

	}

}

public interface AppleBuilder {
	void setColor(String color);

	Apple getApple();
}
public class ConcreteBuilder implements AppleBuilder {

	private Apple appleA = new ConcreteApple();

	@Override
	public Apple getApple() {
		return appleA;
	}

	public void setColor(String color) {
		appleA.setColor(color);
	}
}
public class Director {

	private ConcreteBuilder appleBuilder = new ConcreteBuilder();

	public Apple getAppleA() {
		appleBuilder.setColor("黑色");
		return appleBuilder.getApple();
	}
	public Apple getAppleB() {
		appleBuilder.setColor("黃色");
		return appleBuilder.getApple();
	}
}
public static void main(String[] args) {
		Director director = new Director();
		//來個A類型的蘋果
		director.getAppleA().eat();
		//來個B類型的蘋果
		director.getAppleB().eat();
	}

運行結果:
在這裏插入圖片描述
總結:核心思想就是 增加一個建造層,屏蔽 高層用戶 對 具體產品的 細節。如上,蘋果是產品,有顏色的區別,A類蘋果是黑色,B類蘋果是黃色。如果我們不增加中間層,直接new的話就是如下:

public static void main(String[] args) {
		Apple apple=new ConcreteApple();
		apple.setColor("黑色");
		apple.eat();
		System.out.println("-------------------------------");
		Director director = new Director();
		//來個A類型的蘋果
		director.getAppleA().eat();
		//來個B類型的蘋果
		director.getAppleB().eat();
	}

雖然運行結果是一樣,但是用戶還要自己去設置蘋果的顏色,顯然是不合理的,這個屬性是不應該讓用戶去知曉的。增加了建造者就不同,用戶只要說給我一個A類蘋果就行了。
具體類圖:
在這裏插入圖片描述
蘋果就是產品類。
代理模式:爲其他對象提供一種代理以控制對這個對象的訪問。也叫委託模式。
代理模式比較簡單,想象成皮包公司就好了。

public interface IEat {
	void eat();
}

public class ConcreteEat implements IEat{

	@Override
	public void eat() {
		System.out.println("表演用手喫飯");
	}
	
}
public class EatProxy implements IEat {
	private IEat eat = new ConcreteEat();
	@Override
	public void eat() {
		this.eat.eat();
	}
}
public class ProxyTest {
	public static void main(String[] args) {
		IEat proxy = new EatProxy();
		proxy.eat();
	}
}

運行結果:
在這裏插入圖片描述
可以看到 客戶端根本就不知道 具體的喫類,知道代理,但是最終幹活的不是代理,記住 代理=皮包公司 皮包公司是沒有生成產品的能力的。
類圖:
在這裏插入圖片描述
代理模式可以擴展出:
普通代理:要求客戶端只能訪問代理角色,而不能訪問真實角色。
強制代理:通過真實角色找到代理角色。
動態代理:AOP面向橫切面編程、實現InvocationHandler接口。切面Aspect,切入點JoinPoint,通知advice,織入Weave.
原型模式:用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。實現Cloneable接口,重寫clone方法。
原型模式比較簡單就是利用對象的clone方法複製對象,已達到快速創建對象的目的。
簡單實現:

public class Apple implements Cloneable{
 String name;
 void eat(){
  System.out.println("我是"+name+"水果!");
 }
 void setName(String name) {
  this.name=name;
 }
 @Override
 protected Object clone(){
  Apple fruit=null;
  try {
   fruit=(Apple) super.clone();
  } catch (CloneNotSupportedException e) {
   e.printStackTrace();
  }
  return fruit;
 }
}
public static void main(String[] args) {
  Apple appleA=new Apple();
  appleA.setName("紅蘋果");
  appleA.eat();
  Apple appleB=(Apple) appleA.clone();
  appleB.setName("青蘋果");
  appleB.eat();
 }

運行結果:
在這裏插入圖片描述
可以看到我們第二個對象不是通過new出來的。這裏可以引申出對象的創建有
1.new
2.clone克隆
3.newInstance反射
4.parcel/Serializable反序列化
基本類圖:
在這裏插入圖片描述
注意事項:使用clone 類的構造函數不會執行。2淺拷貝,深拷貝。對象內部的數組,引用對象不會拷貝,還是指向原生對象的內部元素地址,這就是淺拷貝。深拷貝也很簡單就是數組對象也都自己拷貝一遍如:
在這裏插入圖片描述

3.不用使用final關鍵字。

中介者模式:用一箇中介對象封裝一系列的對象交互,中介使用個對象不需要顯示的相互作用,從而使其耦合鬆散,而且可以獨立的改變它們之間的交互。
核心就是增加一個類處理公共的事情。就是本來A做一件事情a()需要從B獲取信息然後處理。B中做一件事情b()需要從A獲取信息然後處理。但是A和B是同級類 且沒什麼聯繫。
這個時候我們就可以增加一個C類,C類持有A,B,A.B持有C,然後當前A需要做a()時,直接交給C去執行。除了書中的 銷售 庫存 採購 暫時想不起其他例子。。。
類圖:
在這裏插入圖片描述

在這裏插入圖片描述
但是這種模式缺點也很明顯就是 中介者這個類 會越來越大,而且這個模式,引用都是具體實現 違反了 里氏替換,依賴倒置等
由於字數限制,只能轉下一篇。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章