結(jié)構(gòu)型設(shè)計(jì)模式概述
結(jié)構(gòu)型設(shè)計(jì)模式用于處理類或?qū)ο笾g的組合,即描述類和對象之間怎樣組織起來形成大的結(jié)構(gòu),從而實(shí)現(xiàn)新的功能。
實(shí)現(xiàn)的機(jī)制:
結(jié)構(gòu)型對象模式采用組合/聚合機(jī)制來組合類,包括橋梁模式(Bridge)、組合模式(Composite)、裝飾器模式(Decorator)、外觀模式(Facade)、享元模式(FlyWeight)、代理模式(PRoxy)。
結(jié)構(gòu)型類模型采用繼承機(jī)制來組合類,包括適配器模式(Adapter)。
(一)外觀(Facade)模式(門面模式)
問題提出:
在軟件系統(tǒng)中,客戶程序經(jīng)常會與復(fù)雜系統(tǒng)的內(nèi)部子系統(tǒng)之間產(chǎn)生耦合,而導(dǎo)致客戶程序隨著子系統(tǒng)的變化而變化。那么,如何簡化客戶程序與子系統(tǒng)之間的交互接口?如何將復(fù)雜系統(tǒng)的內(nèi)部子系統(tǒng)與客戶程序之間的依賴解耦?
(一)外觀(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完成一項(xiàng)業(yè)務(wù)功能,使用Class3、Class1完成另一項(xiàng)業(yè)務(wù)功能。
public class ClientNoFacade {
public void methodA() {//完成第一項(xiàng)業(yè)務(wù)功能
Class1 c1 = new Class1();
c1.method1();
Class2 c2 = new Class2();
c2.method2();
Class4 c4 = new Class4();
c4.method4();
}
public void methodB() {//完成第二項(xiàng)業(yè)務(wù)功能
Class3 c3 = new Class3();
c3.method3();
Class1 c1 = new Class1();
c1.method1(); }
}
(二)裝飾器(Decorator)模式
問題提出:在軟件系統(tǒng)中,有時(shí)候我們會使用繼承來擴(kuò)展對象的功能,但是由于繼承為類型引入的靜態(tài)特質(zhì),使得這種擴(kuò)展方式缺乏靈活性;并且隨著子類的增多(擴(kuò)展功能的增多),各種子類的組合(擴(kuò)展功能的組合)會導(dǎo)致更多子類的膨脹。如何使“對象功能的擴(kuò)展”能夠根據(jù)需要來動(dòng)態(tài)地實(shí)現(xiàn)?同時(shí)避免“擴(kuò)展功能的增多”帶來的子類膨脹問題?
考慮場景:
星巴克的分店幾乎開遍世界各地。它們提供了各式各樣的美味咖啡:愛爾蘭咖啡、藍(lán)山咖啡、卡布基諾、雀巢。每樣咖啡都有自己的描述屬性和收費(fèi)行為。另外它們還提供各種配料:奶、砂糖、冰塊、豆?jié){。不同的咖啡加入不同的配料計(jì)算的價(jià)格是不一樣的。
如何設(shè)計(jì)?
用繼承的方式實(shí)現(xiàn)簡直是垃圾。
我們換一種思路:
現(xiàn)在用戶點(diǎn)了一杯愛爾蘭雙倍牛奶咖啡,我們要做的是:
創(chuàng)建一個(gè)愛爾蘭咖啡對象
用奶裝飾它
再用奶裝飾它
調(diào)用cost()方法,并依賴委托將配料的價(jià)格計(jì)算進(jìn)去
代碼實(shí)現(xiàn):
//咖啡接口
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();}
}
//藍(lán)山咖啡
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());}
}
在裝飾模式中的各個(gè)角色有:
抽象構(gòu)件(Component)角色:給出一個(gè)抽象接口,以規(guī)范準(zhǔn)備接收附加責(zé)任的對象。
具體構(gòu)件(Concrete Component)角色:定義一個(gè)將要接收附加責(zé)任的類。
裝飾(Decorator)角色:持有一個(gè)構(gòu)件(Component)對象的實(shí)例,并定義一個(gè)與抽象構(gòu)件接口一致的接口。
具體裝飾(Concrete Decorator)角色:負(fù)責(zé)給構(gòu)件對象"貼上"附加的責(zé)任。
(三)靜態(tài)代理(Proxy)模式
問題提出:在軟件系統(tǒng)中,有些對象有時(shí)候由于跨越網(wǎng)絡(luò)或者其他的障礙,而不能夠或者不想直接訪問另一個(gè)對象,如果直接訪問會給系統(tǒng)帶來不必要的復(fù)雜性,這時(shí)候可以在客戶程序和目標(biāo)對象之間增加一層中間層,讓代理對象來代替目標(biāo)對象打點(diǎn)一切。
例如:
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)模式
問題提出:
在軟件系統(tǒng)中,某些類型由于自身的邏輯,它具有兩個(gè)或多個(gè)維度的變化,那么如何應(yīng)對這種“多維度的變化”?如何利用面向?qū)ο蟮募夹g(shù)來使得該類型能夠輕松的沿著多個(gè)方向進(jìn)行變化,而又不引入額外的復(fù)雜度?
例如:
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("畫實(shí)線");}
}
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)模式(類的,對象的適配器模式)
問題的提出:
把一個(gè)類的接口變換成客戶端所期待的另一種接口,從而使原本因接口原因不匹配而無法一起工作的兩個(gè)類能夠一起工作。
假設(shè)我們要打樁,有兩種類:方形樁 和圓形樁.
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);
}
}
現(xiàn)在有一個(gè)應(yīng)用,需要既打方形樁,又打圓形樁.那么我們需要將這兩個(gè)沒有關(guān)系的類綜合應(yīng)用.假設(shè)RoundPeg我們沒有源代碼,或源代碼我們不想修改,那么我們使用Adapter來實(shí)現(xiàn)這個(gè)應(yīng)用
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(目標(biāo)SquarePeg)進(jìn)行適配.
實(shí)際上這是將組合方法(composition)和繼承(inheritance)方法綜合運(yùn)用.
PegAdapter是繼承了SquarePeg,如果我們需要兩邊繼承,即繼承SquarePeg 又繼承RoundPeg,因?yàn)?a href="http://www.companysz.com/article.asp?typeid=160">java中不允許多繼承,但是我們可以實(shí)現(xiàn)(implements)兩個(gè)接口(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;
// 構(gòu)造方法
public PegAdapter(RoundPeg roundPeg, SquarePeg squarePeg){
this.roundPeg=roundPeg;
this.squarePeg=squarePeg;
}
}
新聞熱點(diǎn)
疑難解答
圖片精選