結構型設計模式概述
結構型設計模式用于處理類或對象之間的組合,即描述類和對象之間怎樣組織起來形成大的結構,從而實現新的功能。
實現的機制:
結構型對象模式采用組合/聚合機制來組合類,包括橋梁模式(Bridge)、組合模式(Composite)、裝飾器模式(Decorator)、外觀模式(Facade)、享元模式(FlyWeight)、代理模式(PRoxy)。
結構型類模型采用繼承機制來組合類,包括適配器模式(Adapter)。
(一)外觀(Facade)模式(門面模式)
問題提出:
在軟件系統中,客戶程序經常會與復雜系統的內部子系統之間產生耦合,而導致客戶程序隨著子系統的變化而變化。那么,如何簡化客戶程序與子系統之間的交互接口?如何將復雜系統的內部子系統與客戶程序之間的依賴解耦?
(一)外觀(Facade)模式
public class Class1 {
public void method1(){
….
}
}
public class Class2 {
public void method2(){
….
}
}
public class Class3 {
public void method3(){
….
}
}
public class Class4 {
public void method4(){
….
}
}
假如客戶程序要使用Class1、Class2、Class4完成一項業務功能,使用Class3、Class1完成另一項業務功能。
public class ClientNoFacade {
public void methodA() {//完成第一項業務功能
Class1 c1 = new Class1();
c1.method1();
Class2 c2 = new Class2();
c2.method2();
Class4 c4 = new Class4();
c4.method4();
}
public void methodB() {//完成第二項業務功能
Class3 c3 = new Class3();
c3.method3();
Class1 c1 = new Class1();
c1.method1(); }
}
(二)裝飾器(Decorator)模式
問題提出:在軟件系統中,有時候我們會使用繼承來擴展對象的功能,但是由于繼承為類型引入的靜態特質,使得這種擴展方式缺乏靈活性;并且隨著子類的增多(擴展功能的增多),各種子類的組合(擴展功能的組合)會導致更多子類的膨脹。如何使“對象功能的擴展”能夠根據需要來動態地實現?同時避免“擴展功能的增多”帶來的子類膨脹問題?
考慮場景:
星巴克的分店幾乎開遍世界各地。它們提供了各式各樣的美味咖啡:愛爾蘭咖啡、藍山咖啡、卡布基諾、雀巢。每樣咖啡都有自己的描述屬性和收費行為。另外它們還提供各種配料:奶、砂糖、冰塊、豆漿。不同的咖啡加入不同的配料計算的價格是不一樣的。
如何設計?
用繼承的方式實現簡直是垃圾。
我們換一種思路:
現在用戶點了一杯愛爾蘭雙倍牛奶咖啡,我們要做的是:
創建一個愛爾蘭咖啡對象
用奶裝飾它
再用奶裝飾它
調用cost()方法,并依賴委托將配料的價格計算進去
代碼實現:
//咖啡接口
package com.lovo.decoretor;
public interface Coffee {public int cost();
}
//裝飾者類
package com.lovo.decoretor;
public abstract class Drcoretor implements Coffee {
@Overridepublic abstractint cost();
}
//配料
package com.lovo.decoretor;
public class BingDecoretor extends Drcoretor {
private Coffee coffee;public BingDecoretor(Coffee coffee){this.coffee = coffee;}
public int cost() {// TODO Auto-generated method stubreturn 2+coffee.cost();}
}
//配料
package com.lovo.decoretor;
public class NaiDecoretor extends Drcoretor {
private Coffee coffee;public NaiDecoretor(Coffee coffee){this.coffee = coffee;}public int cost() {// TODO Auto-generated method stubreturn 1+coffee.cost();}
}
//藍山咖啡
package com.lovo.decoretor;
public class LanShanCoffee implements Coffee{
@Overridepublic int cost() {// TODO Auto-generated method stubreturn 10;}
}
//測試
package com.lovo.decoretor;
public class TestDecoretor {public static void main(String[] args) {Coffee coffee = new LanShanCoffee();//System.out.println(coffee.cost());coffee = new NaiDecoretor(coffee);coffee=new BingDecoretor(coffee);System.out.println(coffee.cost());}
}
在裝飾模式中的各個角色有:
抽象構件(Component)角色:給出一個抽象接口,以規范準備接收附加責任的對象。
具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
裝飾(Decorator)角色:持有一個構件(Component)對象的實例,并定義一個與抽象構件接口一致的接口。
具體裝飾(Concrete Decorator)角色:負責給構件對象"貼上"附加的責任。
(三)靜態代理(Proxy)模式
問題提出:在軟件系統中,有些對象有時候由于跨越網絡或者其他的障礙,而不能夠或者不想直接訪問另一個對象,如果直接訪問會給系統帶來不必要的復雜性,這時候可以在客戶程序和目標對象之間增加一層中間層,讓代理對象來代替目標對象打點一切。
例如:
package com.lovo.proxy;
public interface Book {
public void SellBook();}
//代理
package com.lovo.proxy;
public class BookProxy implements Book{
private RealSubject r;public BookProxy(RealSubject r) {super();this.r = r;//r = new RealSubject();}
@Overridepublic void SellBook() {r.SellBook();}
}
package com.lovo.proxy;
public class RealSubject implements Book{
@Overridepublic void SellBook() { System.out.println("賣書");}
}
//測試
package com.lovo.proxy;
public class Test {
public static void main(String[] args) {Book b = new BookProxy(new RealSubject());b.SellBook();}}
(四)橋梁(Bridge)模式
問題提出:
在軟件系統中,某些類型由于自身的邏輯,它具有兩個或多個維度的變化,那么如何應對這種“多維度的變化”?如何利用面向對象的技術來使得該類型能夠輕松的沿著多個方向進行變化,而又不引入額外的復雜度?
例如:
package com.lovo.Bridge1;
public interface Draw {
public void draw();}
package com.lovo.Bridge1;
public interface Shap {
public void mydraw();}
package com.lovo.Bridge1;
public class HuanShiXian implements Draw{
@Overridepublic void draw() {System.out.println("畫實線");}
}
package com.lovo.Bridge1;
public class HuaXuXian implements Draw{
@Overridepublic void draw() {System.out.println("畫虛線");}
}
package com.lovo.Bridge1;
public class JuXing implements Shap{private int width;private int height;private Draw draw;
public JuXing(int width, int height, Draw draw) {super();this.width = width;this.height = height;this.draw = draw;}
public int getWidth() {return width;}
public void setWidth(int width) {this.width = width;}
public int getHeight() {return height;}
public void setHeight(int height) {this.height = height;}
@Overridepublic void mydraw() {System.out.println("畫矩形:長:"+this.width+",高:"+this.height);draw.draw();}
}
package com.lovo.Bridge1;
public class Test {
public static void main(String[] args) { Draw draw = new HuanShiXian(); Shap s = new JuXing(100, 50, draw);s.mydraw();}}
(五)適配器(Adaptor)模式(類的,對象的適配器模式)
問題的提出:
把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口原因不匹配而無法一起工作的兩個類能夠一起工作。
假設我們要打樁,有兩種類:方形樁 和圓形樁.
public class SquarePeg{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
現在有一個應用,需要既打方形樁,又打圓形樁.那么我們需要將這兩個沒有關系的類綜合應用.假設RoundPeg我們沒有源代碼,或源代碼我們不想修改,那么我們使用Adapter來實現這個應用
public class PegAdapter extends SquarePeg{
private RoundPeg roundPeg;
public PegAdapter(RoundPeg peg) {
this.roundPeg=peg;
}
public void insert(String str) {
roundPeg.insertIntoHole(str);
}
}
上面代碼中,RoundPeg屬于Adaptee,是被適配者.
PegAdapter是Adapter,將Adaptee(被適配者RoundPeg)和Target(目標SquarePeg)進行適配.
實際上這是將組合方法(composition)和繼承(inheritance)方法綜合運用.
PegAdapter是繼承了SquarePeg,如果我們需要兩邊繼承,即繼承SquarePeg 又繼承RoundPeg,因為java中不允許多繼承,但是我們可以實現(implements)兩個接口(interface)
public interface IRoundPeg{
public void insertIntoHole(String msg);
}
public interface ISquarePeg{
public void insert(String str);
}
新的RoundPeg 和SquarePeg
public class SquarePeg implements ISquarePeg{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg implements IRoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
新的PegAdapter
public class PegAdapter implements IRoundPeg, ISquarePeg{
private RoundPeg roundPeg;
private SquarePeg squarePeg;
// 構造方法
public PegAdapter(RoundPeg roundPeg, SquarePeg squarePeg){
this.roundPeg=roundPeg;
this.squarePeg=squarePeg;
}
}
新聞熱點
疑難解答