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同時為不安定因素是優先考慮使用橋樑模式。