1. 程式人生 > >java設計模式學習筆記(二)--- 結構型模式

java設計模式學習筆記(二)--- 結構型模式

文章目錄

介面卡模式

介面卡是一個介面轉換器,用於在接收不同的輸入時,得到一致的輸出。
在java中,可以這樣理解:使擁有不同介面的實現類,在同一個介面下實現一致的輸出。

一般來說,介面卡模式並不是在開發初期需要考慮的設計模式。因為這時候我們只會有一套工具類,不需要做適配。但是隨著專案的推進,可能會加入一些其他的包。若想讓它們實現同一個介面,就可以考慮使用介面卡模式了。

介面卡的實現方法不唯一,只要實現讓介面卡類在不同輸入下完成一致的輸出即可。具體是用繼承還是持有物件的方法,可以視情況而定。

package blog.java.pattern.creater.adapter;

public class AdapterTest{
	public static void main(String[] args) {//測試
		System.out.println(new CommonAdapter_forExtend().doSomething());
		System.out.println(new CommonAdapter_forInclude(new NewClass()).doSomething());
	}
}

interface IOld{public int doSomething
();} //老介面 interface INew{public int doAnything();} //新介面 class OldClass implements IOld{ //老實現類 public int doSomething() { return 1; }} class NewClass implements INew{ //新實現類 public int doAnything() { return 2; }} /** 實用類繼承的方法來實現。使NewClass擁有了IOld的方法。 */ class CommonAdapter_forExtend extends NewClass
implements IOld{ public int doSomething() {//新的實現。內容可能會根據需要做一些更改。 return super.doAnything() * 2; } } /** 持有物件的方式實現 */ class CommonAdapter_forInclude implements IOld{ private IOld iOld; private INew iNew; CommonAdapter_forInclude(IOld iOld){ this.iOld = iOld; } CommonAdapter_forInclude(INew iNew){ this.iNew = iNew; } public int doSomething() { if(this.iOld != null) return this.iOld.doSomething(); else return this.iNew.doAnything() * 2; } }

觀察程式碼可以發現,在適配過程中,無論是OldClass 還是 NewClass 都不需要做修改便實現了目的,這就是介面卡寫法的作用。
繼承是一個很方便的機制,但是在大多數情況下都很危險,慎用!

組合模式

一句話來概括:對一組介面“提取公因式”。

恩,沒了。樹形結構?那是什麼,我不知道。

對於一組類所持有的介面,若它們有共同的方法,可以嘗試將這些共有的方法提取出來組建新的介面。寫一個簡單的例子。

package blog.java.pattern.composite;

public class CompositeTest {}

interface IA{
	public void doA();
	public void doCommon();}
interface IB{
	public void doB();
	public void doCommon();}
interface IC{
	public void doC();
	public void doCommon();}
//↑↑↑↑↑原來的設計↑↑↑↑↑

//↓↓↓↓↓↓新的設計↓↓↓↓↓↓
interface IA_New{
	public void doA();}
interface IB_New{
	public void doB();}
interface IC_New{
	public void doC();}
interface ICommon{
	public void doCommon();}

對於這種結構的介面,一個很自然的想法就是將共有的方法寫到一個父類中,這就是組合模式最基本的寫法。這部分的程式碼就不寫了。

再來看一下上面的寫法(IA,IB,IC的部分),將同樣的方法放到不同的介面,這無疑是冗餘。對於這樣的類,不只是第一次編寫,在日後的維護中都會成為大麻煩。

那麼什麼樣的一組類會擁有這種形式的介面呢?所有類都有共性,很明顯,那就是有所屬關係的一組類。當這組類有更復雜的層次時,也就是說擁有多層的所屬關係時,這種結構又被稱為樹形結構。這也就是組合模式通常的使用場景。

很多文章都是從“樹形結構”出發去講解組合模式的,不過換個角度看,這只是一種最基本思想(消除冗餘)的具體實現罷了。

裝飾模式

對於一個方法,如果想要動態的在這個方法的執行前後新增多個方法,可以考慮使用裝飾模式。

實現方法簡單來說就是,裝飾類與被裝飾類實現同一個介面,並且持有一個被實現類的物件。裝飾類在自己的實現方法中呼叫內部的被裝飾類物件的方法,並在此前後新增自己特有的方法。
由於裝飾類擁有一樣的介面,它們之間可以相互呼叫。這就導致了它的寫法與執行順序看起來比較難懂。它的執行就像一種抽象的遞迴一樣,在同介面的類之間遞迴呼叫。

package blog.java.pattern.decorator;

public class DecoratorTest {
	public static void main(String[] args) {
		IInterface ii = new BaseClass();
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter2(ii);
		ii = new DecoratorAfter(ii);
		ii.baseMethod();
		
		System.out.println("-------------------");
		//與上面的寫法等效,按喜好選擇。
		new DecoratorAfter(new DecoratorAfter2(new DecoratorBefore(new BaseClass()))).baseMethod();
	}
}

interface IInterface{public void baseMethod();} //待裝飾方法介面

class BaseClass implements IInterface{
	public void baseMethod() {	//待裝飾方法
		System.out.println("do base method ");
	}
}

abstract class Decorator extends BaseClass{
	private IInterface iInterface;
	public Decorator(IInterface iInterface){
		this.iInterface = iInterface;
	}
	
	public void baseMethod() {	//裝飾類共有的方法,提出來放到了父類中。
		this.iInterface.baseMethod();
	}
}

class DecoratorBefore extends Decorator{//裝飾類1
	
	public DecoratorBefore(IInterface iInterface) {
		super(iInterface);
	}

	private void doSomethingBefore(){
		System.out.println("do something before ");
	}
	
	public void baseMethod() {
		this.doSomethingBefore();
		super.baseMethod();
	}
}

class DecoratorAfter extends Decorator{//裝飾類2
	
	public DecoratorAfter(IInterface iInterface) {
		super(iInterface);
	}

	private void doSomethingAfter(){
		System.out.println("do something1 after ");
	}
	
	public void baseMethod() {
		super.baseMethod();
		this.doSomethingAfter();
	}
}

class DecoratorAfter2 extends Decorator{//裝飾類3
	
	public DecoratorAfter2(IInterface iInterface) {
		super(iInterface);
	}

	private void doSomethingAfter(){
		System.out.println("do something2 after ");
	}
	
	public void baseMethod() {
		super.baseMethod();
		this.doSomethingAfter();
	}
}

裝飾模式的精髓就在於我不只可以這樣寫

		IInterface ii = new BaseClass();
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter2(ii);
		ii = new DecoratorAfter(ii);
		ii.baseMethod();

還可以這樣寫

		IInterface ii = new BaseClass();
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter2(ii);
		ii = new DecoratorBefore(ii);
		ii = new DecoratorAfter(ii);
		ii = new DecoratorBefore(ii);
		ii.baseMethod();

可以自由的按照需要去組合。

代理模式

我已經寫好了一個物件,但是卻不準備直接對其進行操作。這時可以考慮使用代理模式。

package blog.java.pattern.proxy;

public class Test {
	public static void main(String[] args) {
		IInterface proxy = new Proxy(new BaseClass());
		proxy.doA();
	}
}

interface IInterface{
	public void doA();
	public void doB();
}
class BaseClass implements IInterface{
	public void doA() {}
	public void doB() {}
}
class Proxy implements IInterface{//代理類
	private IInterface proxy;
	public Proxy(IInterface proxy){
		this.proxy = proxy;
	}
	public void doA() {
		this.proxy.doA();
	}
	public void doB() {
		this.proxy.doB();
	}
}

什麼時候使用代理模式

  • BaseClass類是一個不穩定因素。它可能會追加一些新功能(尤其是在某些方法的執行前後新增,就像aop一樣),也可能它的程式碼本身寫的很糟糕,我不想直接使用。這時可利用代理模式將呼叫者與物件解耦的特點,方便修改。
  • 想讓BaseClass這樣的物件延遲載入。原因與實現方法與單例模式中的幾乎一樣,就不寫實現了。
  • 不能或者不想讓呼叫者直接使用物件。主要出現在遠端呼叫上。

享元模式

通俗點說,享元模式就是一個字面意思上的共享池。
有一組物件,它們包含有一樣的內部物件。這時就可以把它們提取出來放入共享池中。

package blog.java.pattern.flyweight;

import java.util.HashMap;
import java.util.Map;

public class Test {
	
	public static void main(String[] args) {
		FlyWeight f1 = Factory.getFlyWeight("f1");
		FlyWeight f2 = Factory.getFlyWeight("f2");
		FlyWeight f3 = Factory.getFlyWeight("f1");
		FlyWeight f4 = Factory.getFlyWeight("f1");
		
		System.out.println(f1.shareObj);
		System.out.println(f2.shareObj);
		System.out.println(f3.shareObj);
		System.out.println(f4.shareObj);
	}
}

class FlyWeight{
	String flag;		//標識物件
	Object shareObj;	//共享物件
	Object unShareObj;	//非共享物件
	public FlyWeight(String flag, Object shareObj) {
		this.flag = flag;
		this.shareObj = shareObj;
	}
}

class Factory{	//工廠類
	private static Map<String, Object> map = new HashMap();
	public static FlyWeight getFlyWeight(String flag){
		Object temp = map.get(flag);
		if(temp == null){
			temp = new Object();
			map.put(flag, temp);
		}
		return new FlyWeight(flag, temp);
	}
}

上面的例子中,假定如果flag相同,那麼它們就有一樣的shareObj,並且這個shareObj是一個邏輯上允許共享的物件。

在建立4個FlyWeight的例項後,只有2個shareObj的例項存在於記憶體中。

相比於一般的使用方法,享元模式會使程式變得更復雜,並且幾乎不會增強功能性。因此,享元模式主要適用於需要降低記憶體使用的情況下。

外觀模式(門面模式)

我想進行一個複雜操作,但是不想管裡面的實現,想要只通過一個呼叫就能得到結果。這時用外觀模式。

這個應該所有人都用過,我們平時寫的各種工具類就是一個外觀模式。不多說了。

package blog.java.pattern.facade;

public class FacadeTest {
	public static void main(String[] args) {
		new Facade().doSomething("do something");
	}
}

class Facade{
	public void doSomething(Object obj){
		System.out.println("do something A");
		System.out.println("do something B");
		System.out.println(obj);
		System.out.println("do something C");
		System.out.println("do something D");
	}
}

橋樑模式

從寫法上看,它跟策略模式沒什麼區別,就是增加了一個抽象父類。想要達到的效果也一樣,就是解耦。
為了讓寫出的程式碼看起來更像橋樑模式,需要把抽象出來的,不會改變的部分拿到父類中去。

package blog.java.pattern.bridge;

public class Bridge {
	public static void main(String[] args) {
		BaseClass baseClass = new BaseClass(new MethodClass());
		baseClass.doSomething();
	}
}

interface IInterface{public void doA();}

abstract class AbstractClass{
	IInterface iInterface;
	public AbstractClass(IInterface iInterface){
		this.iInterface = iInterface;
	}
	public void doSomething(){	//抽象出的業務
		System.out.println("do 1");
		this.iInterface.doA();
		System.out.println("do 2");
	}
}

class BaseClass extends AbstractClass{
	public BaseClass(IInterface iInterface) {
		super(iInterface);
	}
}

class MethodClass implements IInterface{//具體實現
	public void doA() {
		System.out.println("do A");
	}
}

一般在BaseClass與MethodClass同時為不安定因素是優先考慮使用橋樑模式。