設計模式精髓篇之結構型

結構型模式

主要是對類的結構和類與類之間的關係進行的設計,採用類間或者接口與類間的關聯組合或依賴來實現爲對象添加新的功能。

續前2篇

設計模式初篇
設計模式精髓篇之創建型

裝飾者模式

目標: 通過類之間關聯的方式,動態地給一個對象添加額外的功能和職責。(可以理解爲在已有的函數功能上再添加上額外的職責)
why-思考: 一般對一個對象添加額外的功能,採用繼承的方式可以解決,但是也會因爲繼承的方式把父類的其他方法功能也繼承下來,這不是我們擴展對象的目的。怎麼通過類之間的關聯方式繼承來實現尼並且做到不會像繼承一樣帶來副作用尼?
how-思考: 分清角色,需要添加功能的對象(被裝飾者)和實現要添加功能的對象(裝飾者)。使用對象之間的關聯方式,在原來的基礎上添加額外的功能。
JDK中的示例:BufferedInputStream(裝飾者)、InputerStream(被裝飾者)

//1.定義你需要動態添加功能的對象的類——被裝飾者
public abstract class InputStream extends Object implements Closeable {
   ......//其他函數
   //1.1需要增加功能的函數部分
   public int read(byte[] buffer, int offset, int length) throws IOException {
        //從輸入流中讀取字節並存入到字節數組buffer中
        Arrays.checkOffsetAndCount(buffer.length, offset, length);
        for (int i = 0; i < length; i++) {
            int c;
            try {
                if ((c = read()) == -1) {
                    return i == 0 ? -1 : i;
                }
            } catch (IOException e) {
                if (i != 0) {
                    return i;
                }
                throw e;
            }
            buffer[offset + i] = (byte) c;
        }
        return length;
    }
//2.定義實現要添加功能的對象的類——裝飾者
//2.1抽象裝飾者FilterInputStream,需要將被裝飾者關聯。
FilterInputStream extends InputStream {
     protected volatile InputStream in;
     protected FilterInputStream(InputStream in) {
        this.in = in;
    }
}
//3.實現動態添加功能——具體裝飾者
public class BufferedInputStream extends FilterInputStream{
     public BufferedInputStream(InputStream in, int size) {
        super(in);  
     }
     //3.1在原來的read基礎上,實現添加額外功能
     @Override 
     public synchronized int read(byte[] buffer, int offset, int byteCount) throws IOException {
      ......
      InputStream localIn = in;
        while (true) {
            int read;
            if (markpos == -1 && required >= localBuf.length) {
                //3.2原來的read函數,其他部分便是動態添加的代碼功能
                read = localIn.read(buffer, offset, required);
                if (read == -1) {
                    return required == byteCount ? -1 : byteCount - required;
                }
            }
       ......
}
//4.最後實現,爲InputStream對象添加了BufferedInputStream 中的功能
InputStream bufferInput=new BufferedInputStream (new InputStream(),1024)
適配器模式

目標: 將一個類的接口變換成客戶端所期待的另外一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作.
why-思考: 在軟件系統中,如何將一些已有的現存對象放到新的不同接口的環境中還能繼續工作尼?
how-思考: 想辦法將兩種接口相結合,一方面能用到原來的對象和方法,可以將適配者依賴原來的對象,另一方面需要適應新的環境,需要實現新的接口方法。
JDK中的示例: 適配者InputStreamReader,新的環境Reader

//1.要適應的新環境Reader字符流
public abstract class Reader implements Readable, Closeable {
 ......
     public abstract int read(char[] buf, int offset, int count) throws IOException;
 ......
}
//2.現有的對象和方法
public abstract class InputStream extends Object implements Closeable {
   ......//其他函數
    public int read(byte[] buffer, int offset, int length) throws IOException {
    ......//省略的代碼
    }
   }
//3.定義適配者InputStreamReader,去適應新環境
public class InputStreamReader extends Reader{
    //3.2需要將舊環境中的對象給關聯進來
    private InputStream in;
    public InputStreamReader(InputStream in, final String charsetName){
    .....
    this.in=in;
    ......
    }
    @Override
    public int read(char[] buffer, int offset, int length) throws IOException {
      //3.3將現有改成適應新的環境的接口
       .....
       int actualByteCount = in.read(bytes.array(), off, desiredByteCount);
       .....      
    }
}
代理模式

目標: 爲其他對象提供一種代理以控制對這個對象的訪問
why-思考: 如何對訪問一個類的對象時加以控制?
how-思考: 在訪問者和被訪問者間加上中間層。
JDK中的示例: 分爲靜態代理和動態代理

//--------------------靜態代理,JDK中暫時沒有找到
//1.定義對象對外提供的代理訪問方法的接口
public interface IXXDao{
  public void dothing();
}
//2.被代理的對象類,也是目標對象
public XXDao implements IXXDao{
 public void dothing(){
  ......//do something
 }
}
//3.代理對象,中間件
public XXProxy implements IXXDao{
  //3.1 代理對象,需要關聯目標對象
  IXXDao dao;
  public void setIXXDao(IXXDao dao){
  this.dao=dao;
  }
  //3.2 具體的代理操作
  public void dothing(){
  ......//控制對dao的訪問的代碼
  dao.dothing();
 }
}

//--------------------動態代理,JDK中java.lang.reflect.Proxy
//說明
//1.代理對象,不需要實現接口,通過JDK中的API動態生成代理對象,但是需要我們指定創建代理對象和目標對象實現的接口的類型。
//2.目標對象(被代理的對象一定要實現接口。
//已經定義好目標對象dao(XXDao)
//使用Proxy.newProxyInstance創建代理對象
IXXDao proxy=(IXXDao)Proxy.newProxyInstance(dao.getClass().getClassLoader(),dao.getClass().getInterfaces(),new InvocationHandler() {            
        @Override
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
                System.out.println("do before......");  
                Object object=method.invoke(dao, args);
                System.out.println("do after......");
                return object;
            }
        });
    //代理實現
    proxy.dothing();
    //newProxyInstance中參數說明:
    //1. 類加載器(Class Loader)目標對象的類的加載器
    //2. 需要實現的接口數組,(目標對象實現的接口數組)
    //3. InvocationHandler接口。所有動態代理類的方法調用,都會交由InvocationHandler接口實現類裏的invoke()方法去處理。
組合模式

目標: 將對象組合成樹形結構以表示“部分和整體”的層次結構,使用戶對單個對象和組合對象的使用性保持具有一致性。
why-思考: 如何構建一個樹形結構?怎麼使得單個對象和組合對象使用方法保持一致?
how-思考: 使用類之間的組合關係。樹枝和葉子實現統一接口,樹枝內部組合該接口。
JDK中的示例: javax.swing.JComponent

//1.定義整體和部分共同的特徵並抽象爲一個類——
public abstract class Component implements ImageObserver, MenuContainer, Serializable                                  
{
   public void setFont(Font f) {
            Component.this.setFont(f);
        }
}
//2.具體的組合對象
public class Container extends Component {
private List<Component>component=new ArrayList<Component>();
      public void setFont(Font f) {
        boolean shouldinvalidate = false;
        Font oldfont = getFont();
        super.setFont(f);
        Font newfont = getFont();
        if (newfont != oldfont && (oldfont ==null                                ||!oldfont.equals(newfont))) {
        // 將component每一個都setFont
            invalidateTree();
    }    
}
//3.單個對象,最終祖類還是Component 
public class JLabel extends JComponent{
  .....
  //依然可以調用setFont函數,只不過在祖類中實現了
}

感想

1.主要總結了結構型中常用的幾種模式。
2.學會合理的使用類之間的組合和依賴有時候比類之間的繼承效果來的更好。
3.類之間的組合和依賴,儘量要解耦,不要依賴或者關聯具體的類。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章