主頁 > 軟體設計 > 設計模式學習(上)-Java

設計模式學習(上)-Java

2020-09-24 03:54:50 軟體設計

目錄
  • 設計模式學習(Java實作)
    • 1. 什么是設計模式
      • 設計模式的意義:
      • 使用設計模式的優點:
      • 設計模式的基本要素:
      • 23種設計模式:
    • 2. OOP的七大原則
    • 3.單例模式
      • A.餓漢式單例模式
      • B.懶漢式單例模式
      • C.懶漢式單例模式-單鎖
      • D.懶漢式單例模式-雙重檢測鎖(DCL)
      • E.靜態內部類單例模式
      • F.列舉類單例模式
    • 4.工廠模式
      • 工廠模式的作用:
      • 工廠模式的詳細分類:
      • 核心本質:
      • 三種模式:
      • 看代碼其實會清晰很多:
      • 簡單工廠模式vs工廠方法模式:
    • 5.抽象工廠模式
      • 定義:
      • 適用場景:
      • 代碼演示:
      • 抽象工廠模式的優點;
      • 抽象工廠模式的缺點:
    • 6.建造者模式
      • 定義:
      • 主要作用:
      • 舉個具體例子:
        • 角色分析:
        • 通過代碼來康康:
      • 對建造者模式的一些補充說明:
      • 另一種方式:
        • 舉個例子:
      • 建造者模式的優點
      • 建造者模式的缺點
      • 建造者模式vs抽象工廠模式
    • 7.原型模式
      • 先講講深拷貝與淺拷貝的區別:
      • 直接上代碼:
    • 8.配接器模式
      • 配接器模式的作用
      • 角色分析
      • 代碼演示
      • 物件配接器的優點:
      • 類配接器的缺點:
    • 9.橋接模式
      • 定義:
      • 多層繼承結構vs橋接模式:
      • 上代碼演示一下:
      • 橋接模式的好處:
      • 橋接模式的劣勢:
    • 10.靜態代理模式
      • 角色分析:
      • 上代碼分析-1:
      • 代理模式的好處:
      • 代理模式的缺點:
      • 上代碼分析-2:
    • 11.動態代理模式
      • 角色分析
      • 動態代理的代理類是動態生成的,不是我們直接寫好的
      • 代碼演示
      • 動態代理的好處:

設計模式學習(Java實作)

1. 什么是設計模式

設計模式是對于代碼開發經驗的總結,用于提高代碼的可復用性、可維護性、可讀性、穩健性以及安全性
由GoF(四人幫)合作設計,一共收錄了23種設計模式

設計模式的意義:

設計模式的本質是面向物件設計原則的實際運用,是對類的封裝性、繼承性和多型性以及類的關聯關系和組合關系的充分理解

使用設計模式的優點:

  1. 可以提高程式員的思維能力、編程能力和設計能力
  2. 使程式設計更加標準化、代碼編制更加工程化,使軟體開發效率大大提高,從而縮短軟體的開發周期
  3. 使設計的代碼可重用性高,可讀性強,可靠性高,靈活性好,可維護性強

設計模式的基本要素:

  • 模式名稱
  • 需要解決的問題
  • 解決方案
  • 效果

23種設計模式:

  • 創建型模式:
    (描述如何去創建物件,目的是為了將物件的創建和使用分離)
    單例模式、工廠模式、抽象工廠模式、建造者模式、原型模式
  • 結構型模式:
    (描述如何將類/物件按照某種布局組合成更大的結構)
    配接器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式
  • 行為型模式:
    (描述類/物件之間的協作,以完成單個類無法完成任務,主要是分配職責)
    模板方法模式、命令模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、解釋器模式、狀態模式、策略模式、職責鏈模式、訪問者模式




2. OOP的七大原則

  • 開閉原則:對擴展開放,對修改關閉
    (也就是說擴展的代碼盡量不影響原來的代碼,盡量做到獨立)
  • 里氏替換原則:繼承必須確保超類所擁有的性質在子類中仍然成立
    (也就是說,子類應該是在父類基礎上的拓展,添加新的方法,而盡量不要去修改父類原有的方法等)
  • 依賴倒置原則:要面向介面編程,不要面向實作編程
  • 單一職責原則:控制類的粒度大小,將物件解耦,提高其內聚性
    (也就是說,一個物件不應該承擔很多職責,否則會有很多冗余,粒度太粗)
  • 介面隔離原則:要為各個類建立它們需要的專用介面
  • 迪米特法則:只和直接相關聯的物件通信,而不跟其他物件通信
  • 合成復用原則:盡量先使用組合或者聚合等關聯關系來實作,其次才考慮使用繼承關系來實作


3.單例模式

A.餓漢式單例模式

//餓漢式單例模式
//可能會浪費記憶體

//Hungry.java

public class Hungry{

	//先占用空間,不一定使用,可能浪費
	private byte[] data1 = new byte[1024 * 1024];
	private byte[] data2 = new byte[1024 * 1024];
	private byte[] data3 = new byte[1024 * 1024];
	private byte[] data4 = new byte[1024 * 1024];

	private Hungry(){

	}

	private final static Hungry HUNGRY = new Hungry();

	public static Hungry getInstance(){
		return HUNGRY;
	}
}


B.懶漢式單例模式


//懶漢式單例模式
//有執行緒安全問題

//LazyMan.java

public class LazyMan{
	private LazyMan(){

	}

	private static LazyMan lazyMan;

	public static LazyMan getInstance(){
		if(lazyMan == null){
			lazyMan = new LazyMan();
		}
		return lazyMan;
	}

}

懶漢式單例模式是執行緒不安全的:

//懶漢式單例模式
//有執行緒安全問題

//LazyMan.java

public class LazyMan{
	private LazyMan(){
		System.out.println(Thread.currentThread().getName() + "ok");
	}

	private static LazyMan lazyMan;

	public static LazyMan getInstance(){
		if(lazyMan == null){
			lazyMan = new LazyMan();
		}
		return lazyMan;
	}

	//多執行緒并發
	public static void main(String[] args){
		for(int i = 0;i < 10;i++){
			new Thread(() ->{
				LazyMan.getInstance();
			}).start();
		}
	}
}

那么,為了解決懶漢式單例模式的執行緒不安全問題,我們就應該給他加鎖,


加鎖也有兩種模式:

C.懶漢式單例模式-單鎖


//LazyMan.java

public class LazyMan{
	private LazyMan(){
		System.out.println(Thread.currentThread().getName() + "ok");
	}

	private static LazyMan lazyMan;

	public static LazyMan getInstance(){

		if(lazyMan == null){
			synchronized(LazyMan.class){
				lazyMan = new LazyMan();
			}
		}
		return lazyMan;
	}

	//多執行緒并發
	public static void main(String[] args){
		for(int i = 0;i < 10;i++){
			new Thread(() ->{
				LazyMan.getInstance();
			}).start();
		}
	}
}



D.懶漢式單例模式-雙重檢測鎖(DCL)


//雙重檢測鎖模式的懶漢式單例模式(即DCL懶漢式)
//通過加鎖來解決懶漢式單例模式的執行緒不安全問題

//LazyMan.java

public class LazyMan{
	private LazyMan(){
		System.out.println(Thread.currentThread().getName() + "ok");
	}

	private volatile static LazyMan lazyMan;

	public static LazyMan getInstance(){

		if(lazyMan == null){
			synchronized(LazyMan.class){
				if(lazyMan == null){
					lazyMan = new LazyMan();
				}
			}
		}
		return lazyMan;
	}

	//多執行緒并發
	public static void main(String[] args){
		for(int i = 0;i < 10;i++){
			new Thread(() ->{
				LazyMan.getInstance();
			}).start();
		}
	}
}


關于volatile:

其實,嚴格來說,就算是DCL懶漢式單例模式也是執行緒不安全的,為什么呢?
因為lazyMan = new LazyMan()這個操作并不是原子性操作,它需要走三步:

  1. 分配記憶體空間
  2. 執行構造方法,初始化物件
  3. 把這個物件指向所分配的空間


    所以有可能發生指令重排!
    可能執行順序是:123
    也可能是:312
    所以需要volatile~

E.靜態內部類單例模式


public class Holder{

	private Holder(){

	}

	public static Holder getInstance(){
		return InnnerClass.HOLDER;
	}

	public static class InnnerClass{
		private static final Holder HOLDER = new Holder();
	}
}


以上單例模式都不安全,都能被反射破壞,所以要看看列舉類單例模式!它不會被反射破壞!


F.列舉類單例模式



//EnumSingle.java

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;


public enum EnumSingle{
	INSTANCE;

	public EnumSingle getInstance(){
		return INSTANCE;
	}
}

class Test{

	public static void main(String[] args) throws NoSuchMethodException,IllegalAccessException,InvocationTargetException,InstantiationException{
		EnumSingle instance1 = EnumSingle.INSTANCE;
		Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
		declaredConstructor.setAccessible(true);
		EnumSingle instance2 = declaredConstructor.newInstance();

		System.out.println(instance1);
		System.out.println(instance2);
	}
}





4.工廠模式

工廠模式的作用:

實作了創建者和呼叫者的分離

工廠模式的詳細分類:

  • 簡單工廠模式
  • 工廠方法模式
  • 抽象工廠模式

核心本質:

  • 實體化物件不使用new,用工廠方法代替
  • 將選擇實作類、創建物件統一管理和控制,從而將呼叫者跟我們的實作類解耦

三種模式:

  • 簡單工廠模式:(靜態工廠模式)
    用來生產同一等級結構中的任意產品
  • 工廠方法模式:
    用來生產同一等級結構中的固定產品
  • 抽象工廠模式:
    圍繞一個超級工廠創建其他工廠,該超級工廠又稱為其他工廠的工廠
    (下一節拓展開來講)

看代碼其實會清晰很多:


//簡單工廠模式 == 靜態工廠模式
//若果需要增加一個新的產品,在不修改代碼的情況下是做不到的
//不滿足開閉原則
public class CarFactory{
	public static Car getCar(String car){
		if(car.equals("五菱")){
			return new WuLing();
		}
		else if(car.equals("特斯拉")){
			return new Tesla();
		}
		else {
			return null;
		}
	}
}


public Customer{
	public static void main(String[] args){
		//使用new創建
		//Car car = new WuLing();
		//Car car2 = new Tesla();

		//使用工廠創建
		Car car = CarFactory.getCar("五菱");
		Car car = CarFactory.getCar("特斯拉");

		car.name();
		car2.name();
	}
}


//工廠方法模式
//滿足了開閉原則,但是增加了代碼量

public class Consumer{
	public static void main(String[] args){
		Car car = new TeslaFactory().getCar();
		Car car2 = new WulingFactory().getCar();
		Car car3 = new MobaiFactory().getCar();
	}
}


public interface CarFactory{
	Car getCar();
}


public class TeslaFactory implements CarFactory{
	@Override
	public Car getCar(){
		return new Tesla();
	}
}


public class WulingFactory implements CarFactory{
	@Override
	public Car getCar(){
		return new WuLing();
	}
}



public class MobaiFactory implements CarFactory{
	@Override
	public Car getCar(){
		return new Mobai();
	}
}

簡單工廠模式vs工廠方法模式:

結構復雜度:簡單工廠模式較好
代碼復雜度:簡單工廠模式較好
編程復雜度:簡單工廠模式較好
管理復雜度:簡單工廠模式較好

根據設計原則來看:工廠方法模式更好
根據實際業務來看:簡單工廠模式更好





5.抽象工廠模式

定義:

抽象工廠模式提供了一個創建一系列相關或者相互依賴物件的介面,無需指定它們具體的類
(也就是把工廠再進行抽象,建立工廠的工廠)

適用場景:

  • 客戶端(應用層)不依賴于產品類實體如何被創建、實作等細節
  • 強調一系列相關的產品物件(屬于同一產品族)一起使用創建物件需要大量的重復代碼
  • 提供一個產品類的庫,所有的產品以同樣的介面出現,從而使得客戶端不依賴于具體的實作

什么是產品族&產品等級結構呢?

產品族:是指同一個工廠生產的,位于不同產品等級結構中的一組產品(比如iphone、ipad、macbook是一個產品族)

產品等級結構:即產品的繼承結構

圖示:

wjTqeS.png

代碼演示:


//抽象工廠模式

//手機產品介面

//IphoneProduct.java

public interface IphoneProduct{
	void start();
	void shutdown();
	void call();
	void sendMessage();
}

//路由器產品介面

//IrouterProduct.java

public interface IrouterProduct{
	void start();
	void shutdown();
	void openWiFi();
	void setting();
}

//小米手機

//XiaomiPhone.java

public class XiaomiPhone implements IphoneProduct{
	@Override
	public void start(){
		System.out.println("開啟小米手機");
	}

	@Override
	public void shutdown(){
		System.out.println("關閉小米手機");
	}

	@Override
	public void call(){
		System.out.println("小米打電話");
	}

	@Override
	public void sendMessage(){
		System.out.println("小米發資訊");
	}
}


//華為手機

//HuaweiPhone.java

public class HuaweiPhone implements IphoneProduct{
	@Override
	public void start(){
		System.out.println("開啟華為手機");
	}

	@Override
	public void shutdown(){
		System.out.println("關倍訓為手機");
	}

	@Override
	public void call(){
		System.out.println("華為打電話");
	}

	@Override
	public void sendMessage(){
		System.out.println("華為發資訊");
	}
}

//小米路由器

//XiaomiRouter.java

public class XiaomiRouter implements IrouterProduct{
	@Override
	public void start(){
		System.out.println("啟動小米路由器");
	}
	@Override
	public void shutdown(){
		System.out.println("關閉小米路由器");
	}
	@Override
	public void openWiFi(){
		System.out.println("打開小米Wifi");
	}
	@Override
	public void setting(){
		System.out.println("設定小米路由器");
	}
}

//華為路由器

//HuaweiRouter.java

public class HuaweiRouter implements IrouterProduct{
	@Override
	public void start(){
		System.out.println("啟動華為路由器");
	}
	@Override
	public void shutdown(){
		System.out.println("關倍訓為路由器");
	}
	@Override
	public void openWiFi(){
		System.out.println("打開華為Wifi");
	}
	@Override
	public void setting(){
		System.out.println("設定華為路由器");
	}
}


//生產抽象產品工廠

//IProductFactory.java

public interface IProductFactory{
	//生產手機
	IphoneProduct iphoneProduct();

	//生產路由器
	IrouterProduct irouterProduct();
}


//XiaomiFactory.java

public class XiaomiFactory implements IProductFactory{
	@Override
	public IphoneProduct iphoneProduct(){
		return new XiaomiPhone();
	}

	@Override
	public IrouterProduct irouterProduct(){
		return new XiaomiRouter();
	}
}


//HuaweiFactory.java

public class HuaweiFactory implements IProductFactory{
	@Override
	public IphoneProduct iphoneProduct(){
		return new HuaweiPhone();
	}
	@Override
	public IrouterProduct irouterProduct(){
		return new HuaweiRouter();
	}
}


//Client.java

public class Client{
	public static void main(String[] args)



	System.out.println("=========小米系列產品=========");
	//小米工廠
	XiaomiFactory xiaomiFactory = new XiaomiFactory();

	IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct();
	iphoneProduct.call();
	iphoneProduct.sendMessage();

	IrouterProduct irouterProduct = xiaomiFactory.irouterProduct();
	irouterProduct.openWiFi();
	irouterProduct.setting();


	System.out.println("=========華為系列產品=========");
	//小米工廠
	HuaweiFactory huaweiFactory = new huaweiFactory();

	IphoneProduct iphoneProduct = huaweiFactory.iphoneProduct();
	iphoneProduct.call();
	iphoneProduct.sendMessage();

	IrouterProduct irouterProduct = huaweiFactory.irouterProduct();
	irouterProduct.openWiFi();
	irouterProduct.setting();

}

抽象工廠模式的優點;

  • 具體產品在應用層的代碼隔離,無需關心創建的細節
  • 將一個系列的產品統一到一起創建

抽象工廠模式的缺點:

  • 規定了所有可能被創建的產品集合,產品族中想要拓展新的產品比較困難(所以會比較適合比較穩定的系統)
  • 增加了系統的抽象性和理解難度




6.建造者模式

建造者模式提供了一種創建物件的最佳方式

用戶只需要給出指定復雜物件的型別和內容,建造者模式復雜按順序創建復雜物件(把內部的建造程序和細節隱藏起來)

定義:

將一個復雜物件的構建和它的表示分離,使得同樣的構建程序可以創建不同的表示

主要作用:

在用戶不知道物件的建造程序和細節的情況下就可能直接創建復雜的物件

舉個具體例子:

想要造一棟房子,就需要建筑公司(指揮者)指揮工人(具體建造者)去造房子(產品)

角色分析:

wjHDu6.png

通過代碼來康康:


//抽象的建造者

//Builder.java

public abstract class Builder{
	abstract void buildA();	//打地基
	abstract void buildB();	//鋼筋
	abstract void buildC();	//鋪電線
	abstract void buildD();	//粉刷

	abstract Product getProduct();
}


//產品:房子

public class Product{
	private String buildA;
	private String buildB;
	private String buildC;
	private String buildD;

	public String getBuildA(){
		return buildA;
	}

	public void setBuildA(String buildA){
		this.buildA = buildA;
	}

	public String getBuildB(){
		return buildB;
	}

	public void setBuildB(String buildB){
		this.buildB = buildB;
	}

	public String getBuildC(){
		return buildC;
	}

	public void setBuildC(String buildC){
		this.buildC = buildC;
	}

	public String getBuildD(){
		return buildD;
	}

	public void setBuildD(String buildD){
		this.buildD = buildD;
	}

	@Override
	public String toString(){
		return "Product{" + 
				"buildA = '" + buildA + '\'' + 
				",buildB = '" + buildB + '\'' + 
				",buildC = '" + buildC + '\'' + 
				",buildD = '" + buildD + '\'' + 
				'}';
	}
}


//Worker.java

public class Worker extends Builder{
	
	private Product product;

	public Worker(){
		product = new Product();
	}

	@Override
	void buildA(){
		product.setBuildA("打地基");
		System.out.println("打地基");
	}

	@Override
	void buildB(){
		product.setBuildB("鋼筋");
		System.out.println("鋼筋");
	}


	@Override
	void buildC(){
		product.setBuildC("鋪電線");
		System.out.println("鋪電線");
	}


	@Override
	void buildD(){
		product.setBuildD("粉刷");
		System.out.println("粉刷");
	}

	@Override
	Product getProduct(){
		return product;
	}
}



//指揮:負責指揮構建一個工程,描述和決定工程如何構建

//Director.java

public class Director{

	//指揮工人按照順序建房子
	public Product build(Builer builder){
		builder.buildA();
		builder.buildB();
		builder.buildC();
		builder.buildD();

		return builder.getProduct();
	}

}


//Test.java

public class Test{
	public static void main(String[] args){
		Director director = new Director();

		Product build = director.build(new Worker());
		System.out.println(build.toString());
	}
}

對建造者模式的一些補充說明:

  • Director類在Builder模式中具有很重要的作用,用于指導具體建造者如何建造產品,控制呼叫先后順序,并向呼叫者回傳完整的產品類,
    有些情況下需要簡化系統結構,可以把Director和抽象建造者結合
  • 通過靜態內部類方式實作零件無序裝配構造,這種方式使用起來更加靈活,更符合定義,
    內部有復雜物件的默認實作,使用時可以根據用戶需求自由定義更改內容,且無需改變具體的構造方式,就能生產出不同的復雜產品


另一種方式:

舉個例子:


//Builder.java

public abstract class Builder{
	abstract Builder buildA(String msg);	//漢堡
	abstract Builder buildB(String msg);	//可樂
	abstract Builder buildC(String msg);	//薯條
	abstract Builder buildD(String msg);	//甜點

	abstract Product getProduct();
}


//Worker.java

public class Worker extends Builder{
	
	private Product product;

	public Worker(){
		product = new Product();
	}

	@Override
	Builder buildA(String msg){
		product.setBuildA(msg);
		return this;
	}

	@Override
	Builder buildB(String msg){
		product.setBuildB(msg);
		return this;
	}


	@Override
	Builder buildC(String msg){
		product.setBuildC(msg);
		return this;
	}


	@Override
	Builder buildD(String msg){
		product.setBuildD(msg);
		return this;
	}

	@Override
	Product getProduct(){
		return product;
	}
}


//產品:套餐
public class Product{
	private String BuildA = "漢堡";
	private String BuildB = "可樂";
	private String BuildC = "薯條";
	private String BuildD = "甜點";

	public String getBuildA(){
		return BuildA;
	}
	public void setBuildA(String buildA){
		BuildA = buildA;
	}


	public String getBuildB(){
		return BuildB;
	}
	public void setBuildB(String buildB){
		BuildB = buildB;
	}


	public String getBuildC(){
		return BuildC;
	}
	public void setBuildC(String buildC){
		BuildC = buildC;
	}


	public String getBuildD(){
		return BuildD;
	}
	public void setBuildD(String buildD){
		BuildD = buildD;
	}

    @Override
	public String toString(){
		return "Product{" + 
				"BuildA = '" + BuildA + '\'' + 
				",BuildB = '" + BuildB + '\'' + 
				",BuildC = '" + BuildC + '\'' + 
				",BuildD = '" + BuildD + '\'' + 
				'}';
	}
}


//Test.java

public class Test{
	public static void main(String[] args){
		//服務員
		Worker worker = new Worker();
		
		//默認搭配套餐
		Product product1 = worker.getProduct();

		//自由搭配套餐:鏈式編程
		Product product2 = worker.buildA(msg:"全家桶").buildB("雪碧").getProduct();

		System.out.println(product1.toString());
		System.out.println(product2.toString());
	}
}

建造者模式的優點

  • 產品的建造和表示分離,實作了解耦,使用建造者模式可以使客戶端不必知道產品內部組成的細節
  • 將復雜的創建步驟分解在不同的方法中,使得創建程序更加清晰
  • 具體的建造者類之間是相互獨立的,這有利于紫銅的擴展,增加新的具體建造者無需修改原有類別庫的代碼,符合“開閉原則”

建造者模式的缺點

  • 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似;如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍收到一定的限制
  • 如果產品的內部變化復雜,可能會導致需要定義很多具體建造者來實作這種變化,導致系統變得很龐大

建造者模式vs抽象工廠模式

  • 與抽象工廠模式相比,建造者模式回傳一個組裝好的完整產品,而抽象工廠模式回傳一系列相關的產品,這些產品位于不同的產品等級結構,構成了一個產品族
  • 在抽象工廠模式中,客戶端實體化工廠類,然后呼叫工廠方法獲取所需產品物件,而在建造者模式中,客戶端可以不呼叫建造者的相關方法,而是通過指揮者類來指導如何生成物件,包括物件的組裝程序和建造步驟,它側重于一步步構造一個復雜物件,回傳一個完整的物件
  • 如果將抽象工廠模式看成汽車配件生產工廠,生產一個產品族的產品,那么建造者模式就是一個汽車組裝工廠,通過對部件的組裝可以回傳一輛完整的汽車




7.原型模式

先講講深拷貝與淺拷貝的區別:

圖示

  • 淺拷貝:

wjjVH0.png

  • 深拷貝:

wjjeEV.png

直接上代碼:

  • 淺拷貝:

//原型模式

/*
1. 實作一個介面:cloneable
2. 重寫一個方法:clone()
*/

import java.util.Date;

public class Video implements Cloneable{
	private String name;
	private Date createTime;

	@Override
	protected Object clone() throws ClassNotFoundException{
		return super.clone();
	}

	Video(){

	}
	public Video(String name,Date createTime){
		this.name = name;
		this.createTime = createTime;
	}
	public String getName(){
		return name;
	}
	public Date getCreateTime(){
		return createTime;
	}


	public void setName(String name){
		this.name = name;
	}


	public void setCreateTime(Date createTime){
		this.createTime = createTime;
	}

	public String toString(){
		return "Video{" + name + '\'' + ", createTime = " + createTime + '}';
	}
}



//淺拷貝
//客戶端:克隆

public class Bilibili{
	public static void main(String[] args){
		//原型物件

		Date date = new Date();
		Video v1 = new Video(name:"濤濤說Java",date);
		Video v2 = (Video) v1.clone();


		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:" + v2.hashCode());

		System.out.println("======================");
		date.setCreateTime(123456);

		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:" + v2.hashCode());

	}
}

  • 深拷貝:

import java.util.Date;

public class Video implements Cloneable{
	private String name;
	private Date createTime;


	//對clone()方法的重寫 實作深克隆
	@Override
	protected Object clone() throws ClassNotFoundException{
		Object obj = super.clone();
		
		Video v = (Video) obj;
		v.createTime = (Date) this.createTime.clone();
	}

	Video(){

	}
	public Video(String name,Date createTime){
		this.name = name;
		this.createTime = createTime;
	}
	public String getName(){
		return name;
	}
	public Date getCreateTime(){
		return createTime;
	}


	public void setName(String name){
		this.name = name;
	}


	public void setCreateTime(Date createTime){
		this.createTime = createTime;
	}

	public String toString(){
		return "Video{" + name + '\'' + ", createTime = " + createTime + '}';
	}
}


//深拷貝(改造clone()方法,也就是將這個物件的屬性也一起克隆)

public class Bilibili{
	public static void main(String[] args){
		//原型物件

		Date date = new Date();
		Video v1 = new Video(name:"濤濤說Java",date);
		Video v2 = (Video) v1.clone();


		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:" + v2.hashCode());

		System.out.println("======================");
		date.setCreateTime(123456);

		System.out.println("v1 => " + v1);
		System.out.println("v1 => hash: " + v1.hashCode());
		System.out.println("v2 => " + v2);
		System.out.println("v2 => hash:" + v2.hashCode());

	}
}





8.配接器模式

配接器模式的作用

將一個類的介面轉換成客戶希望的另一個介面,
Adapter模式使得原本由于介面不兼容而不能一起作業的那些類可以在一起作業

角色分析

  • 目標介面:客戶所期待的介面,目標可以是具體的或抽象的類,也可以是介面
  • 需要適配的類:需要適配的類或配接器類
  • 配接器:通過包裝一個需要適配的物件,把原介面轉換成目標物件

圖示:

wvy1q1.png

代碼演示


//要被適配的類:網線

//Adaptee.java

public class Adaptee{
	public void request(){
		System.out.println("連接網線上網");
	}
}


//Computer.java

public class Computer{

	//電腦需要連接上轉接器才能上網
	public void net(NetToUsb adapter){
		//上網的具體實作,但是得先找一個轉接頭
		adapter.handleRequest();

	}

	public static void main(String[] args){
		Computer computer = new Computer();	//電腦
		Adaptee adaptee = new Adaptee();	//網線
		Adapter adapter = new Adapter();	//轉接器

		computer.net(adapter);

	}
}


//介面轉換器的抽象實作

//NetToUsb.java

public interface NetToUsb{

	//處理請求,網線插到Usb上
	public void handleRequest();

}


//真正的配接器,需要連接USB還有網線

//Adapter.java

//1.繼承(類配接器,單繼承)


public class Adapter extends Adaptee implements NetToUsb{


	@Override
	public void handleRequest(){
		super.request();
		//可以上網了
	}
}

另一種實作方式(better)


//Adapter2.java

//2.組合(物件配接器,常用)

public class Adapter2 implements NetToUsb{

	private Adaptee adaptee;

	public Adapter2(Adaptee adaptee){
		this.adaptee = adaptee;
	}

	@Override
	public void handleRequest(){
		adaptee.request();
		//可以上網了
	}
}


//客戶端類:想上網,但是插不上網線(所以才需要一個配接器,也就是轉接頭)

//Computer.java

public class Computer{

	//電腦需要連接上轉接器才能上網
	public void net(NetToUsb adapter){
		//上網的具體實作,但是得先找一個轉接頭
		adapter.handleRequest();

	}

	public static void main(String[] args){
		Computer computer = new Computer();	//電腦
		Adaptee adaptee = new Adaptee();	//網線
		Adapter2 adapter = new Adapter2(adaptee);	//轉接器

		computer.net(adapter);

	}
}

物件配接器的優點:

  • 一個物件配接器可以把多個不同的配接器適配到同一個目標
  • 可以適配一個適配者的子類,由于配接器和適配者之間是關聯關系,根據“里氏代換原則”,適配者的子類也可以通過該配接器進行適配

類配接器的缺點:

  • 對于Java,C#等不支持多重繼承的語言,一次最多只能適配yige配接器類,不能同時適配多個適配者
  • 在Java,C#等語言中,類配接器模式中的目標抽象類只能為介面,不能為類,其使用具有一定的局限性




9.橋接模式

定義:

是將抽象部分與它的實作部分分離,使它們都可以獨立地變化
又稱為柄體(Handle and Body)模式或介面(interface)模式

多層繼承結構vs橋接模式:

圖示:

  • 多層繼承結構:

wqZ4t1.png

  • 橋接模式:

wqZueH.png

上代碼演示一下:


//橋接模式

public interface Brand{

	void info();

}


//聯想品牌

public class Lenovo implements Brand{
	@Override
	public void info(){
		System.out.print("聯想");
	}
}


//蘋果品牌

public class Apple implements Brand{
	@Override
	public void info(){
		System.out.print("蘋果");
	}
}



//抽象的電腦型別
public abstract Computer{

	//組合
	protected Brand brand;

	public Computer (Brand brand){
		this.brand = brand;
	}

	public void info(){
		//自帶品牌
		brand.info();
	}
}

class Desktop extends Computer{
	public Desktop(Brand brand){
		super(brand);
	}

	@Override
	public void info(){
		super.info();
		System.out.println("臺式機");
	}
}

class Laptop extends Computer{
	public Laptop(Brand brand){
		super(brand);
	}

	@Override
	public void info(){
		super.info();
		System.out.println("筆記本");
	}
}


public class Test{
	public static void main(String[] args){
		//蘋果筆記本
		Computer computer = new Laptop(new Apple());
		computer.info();
		//聯想臺式機
		Computer computer2 = new Desktop(new Lenovo());
		computer2.info(); 
	}
}

橋接模式的好處:

  • 橋接模式是比多繼承方案更好的解決方法,極大地減少了子類的個數,從而降低管理和維護的成本,不像多繼承方案,違背了類的單一職責原則,復用性較差,類的個數也很多;
  • 橋接模式提高了系統的擴充性,在兩個變化維度中任意擴展一個維度,都不需要修改原有系統,符合開閉原則

橋接模式的劣勢:

  • 橋接模式的引入會增加系統的理解與設計模式,由于聚合關聯關系建立在抽象層,要求開發者針對抽象進行設計與編程
  • 橋接模式要求正確識別系統中兩個獨立變化的維度,因此其使用范圍具有一定的局限性




10.靜態代理模式

角色分析:

  • 抽象角色:
  • 真實角色:
  • 代理角色:
  • 客戶:訪問代理物件的人

上代碼分析-1:

  1. 介面

public interface Rent{
	public void rent();
}

  1. 真實角色(更加純粹!)

//房東
public class Host implements Rent{
	public void rent(){
		System.out.println("房東出租房子,");
	}
}

  1. 代理角色

//租客
public class Client{
	public static void main(String[] args){
		//房東要出租房子
		Host host = new Host();
		//直接跟房東租房
		//host.rent();

		//代理
		Proxy proxy = new Proxy(host);

		proxy.rent();//附帶中介自己的附加操作
	}
}

  1. 客戶端訪問代理角色

//中介(代理)
public class Proxy implements Rent{
	private Host host;

	public Proxy(){

	}

	public Proxy(Host host){
		this.host = host;
	}

	public void rent(){
		seeHouse();
		sign();
		host.rent();
		fare();
	}

	//看房
	public void seeHouse(){
		System.out.println("中介帶你看房");
	}


	//簽合同
	public void sign(){
		System.out.println("簽租賃合同");
	}

	//收中介費
	public void fare(){
		System.out.println("收中介費");
	}
}

代理模式的好處:

  • 可以使真實角色的操作更加純粹(只需要專注于自己的業務,而不用在乎公共業務)
  • 公共業務就交給代理角色,實作了業務的分工
  • 公共業務發生擴展的時候,方便集中管理

代理模式的缺點:

  • 一個真實角色就會產生一個代理角色:代碼量會翻倍,所以開發效率會變低

上代碼分析-2:

  1. 介面

//用戶需求
public interface UserService{
	public void add();
	public void delete();
	public void update();
	public void query();
}

  1. 真實角色(更加純粹!)

//真實物件
public class UserServiceImpl implements UserService{
	public void add(){
		//日志
		//System.out.println("使用了add()方法");
		System.out.println("增加了一個用戶");
	}
	public void delete(){
		System.out.println("洗掉了一個用戶");
	}

	public void update(){
		System.out.println("修改了一個用戶");
	}

	public void query(){
		System.out.println("查詢了一個用戶");
	}
}

  1. 代理角色

//代理(日志記錄)

public class UserServiceProxy implements UserService{

	private UserServiceImpl userService;

	public void setUserService(UserServiceImpl userService){
		this.userService = userService;
	}

	public void add(){
		log(msg:"add");
		userService.add();
	}

	public void delete(){
		log(msg:"delete");
		userService.delete();
	}

	public void update(){
		log(msg:"update");
		userService.update();
	}


	public void query(){
		log(msg:"query");
		userService.query();
	}


	public void log(String msg){
		System.out.println("[Debug] 使用了" + msg + "方法");
	}
}


  1. 客戶端訪問代理角色

//客戶端

public class Client{
	public static void main(String[] args){
		UserServiceImpl userService = new UserServiceImpl();

		UserServiceProxy proxy = new UserServiceProxy();
		proxy.setUserService(userService);

		proxy.add();
		proxy.update();
	}
}





11.動態代理模式

角色分析

  • 抽象角色:
  • 真實角色:
  • 代理角色:
  • 客戶:訪問代理物件的人

動態代理的代理類是動態生成的,不是我們直接寫好的

動態代理分為兩大類:

  • 基于介面的動態代理√
    JDK的動態代理
  • 基于類的動態代理
    cglib
  • java位元組碼實作
    javasist

需要了解兩個類:(自己去康康檔案8!)

  • Proxy類
  • InvocationHandler類

代碼演示

  1. 介面

public interface Rent{
	public void rent();
}

  1. 真實角色(更加純粹!)

//房東
public class Host implements Rent{
	public void rent(){
		System.out.println("房東出租房子,");
	}
}

  1. 代理角色

//這個類用來自動生成代理類
public class ProxyInvocationHandler implements InvocationHandler{
	
	//被代理的介面
	private void setRent(Rent rent){
		this.rent = rent;
	}

	//生成得到代理類
	public Object getProxy(){
		return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),h:this);
	}

	//處理代理實體,并回傳結果
	public Object invoke(){
		//動態代理的本質,就是使用反射機制實作
		Object result = method.invoke(rent,args);
		return result;
	}
}

  1. 客戶端訪問代理角色

public class Client{
	public static void main(String[] args){
		//真實角色
		Host host = new Host();

		//代理角色:暫時沒有
		ProxyInvocationHandler pih = new ProxyInvocationHandler();

		//通過呼叫程式處理角色來處理我們要呼叫的介面物件
		pih.setRent(host);

		Rent proxy = (Rent)pih.getProxy();

		proxy.rent();

	}
}

動態代理的好處:

  • 可以使真實角色的操作更加純粹(只需要專注于自己的業務,而不用在乎公共業務)
  • 公共業務就交給代理角色,實作了業務的分工
  • 公共業務發生擴展的時候,方便集中管理
  • 一個動態代理類代理的是一個介面,一般就是對應一類業務
  • 一個動態代理類可以代理多個類,只要是實作了同一個介面即可






轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/116228.html

標籤:設計模式

上一篇:5G需要什么APP小程式?5G改變APP小程式

下一篇:寫一個打開cmd視窗并執行cmd命令的Windows腳本(.bat檔案)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more