2014年4月8日 星期二

設計模式 (九) adapter pattern

adapter pattern又稱轉換器,介面轉換。

adapter pattern 就是將一個類別的介面轉換成客戶希望的另一個介面,也就是讓原本不相容的介面一起工作。

舉例來說,假設有以下的需求

  1. 我希望每個shape類別都有顯示的行為
  2. 使用者不需要知道物件為甚麼形狀
接著大概會出現類似下面的程式碼

public abstract class Shape {

	public abstract void Show();
}

class Rectangle extends Shape
{

	@Override
	public void Show() {
		System.out.println("Rectangle");
	}
	
}

class Triangle extends Shape
{

	@Override
	public void Show() {
		System.out.println("Triangle");
	}
}

UML圖會長這樣

可以很看出,Rectangle和Triangle繼承並實作了Shape

接著在此時又出現了一個需求,需要再多實作一個五芒星的圖型
可是我並不知道如何實作這個功能,所以請教了google 大神之後,我得到了一個第三方的sdk
他的功能很完整,不過實作方法有點不同,如下

public class FiveStar
{
	public void FiveStarDisplay()
	{
		System.out.println("FiveStar");
	}
}

可以發現到,我現在必須使用if else 來判斷該怎麼顯示圖型

	public static void showShap(Object shape)
	{
		if(shape instanceof Shape){
			((Shape)shape).Show();
		}
		else if(shape instanceof FiveStar){
			((FiveStar)shape).FiveStarDisplay();
		}
	}
這是個悲劇的開始,客戶必須區分這是甚麼形狀,只要用到shape就必須多加if else 判斷,
並且或許在未來某天,還可能誤將 shape 轉型轉錯造成app crash。

那麼我們可以做甚麼來避免這場悲劇的發生呢?

先思考一個問題,怎麼做對我們最好?
如果我們有辦法將FiveStar視為Shape那是不是所有的問題都迎刃而解了呢?
可是該怎麼做呢?

看看下面的方式

class FiveStarAdapter extends Shape
{
	private FiveStar star;
	@Override
	public void Show() {
		// TODO Auto-generated method stub
		star.FiveStarDisplay();
	}
	
}

我們將 FiveStar封裝在 FiveStarAdapter裡這樣既達到了實作五芒星的功能,又可以維持客戶不需要區分是甚麼形狀就可以使用,接著就可以把showShap改成這樣


	public static void showShap(Shape shape)
	{
			shape.Show();
	}


程式變得更加簡潔,而且更低耦合度了


結論
Adapter的實作方式就是建立一個具備所需介面的新類別(FiveStarAdapter),然後包裝原有的類別(FiveStar),如此就可以將原有的類別轉型成Shape

沒有留言:

張貼留言