設計模式--簡化解釋(二)——結構型設計模式

1.建立型設計模式
2.結構型設計模式
3.行爲型設計模式php

結構型設計模式

簡而言之java

結構模式主要涉及對象的組成,或者是實體如何相互使用。或者,另外一個解釋是,他們幫助回答「如何構建一個軟件組件?」

維基百科說mongodb

在軟件工程中,結構設計模式是經過識別實體之間關係的簡單方法來簡化設計的設計模式。

?適配器模式

現實舉例編程

考慮一下你的記憶卡上有一些圖片,你須要把它們轉移到你的電腦上。爲了轉移它們,你須要某種與你的計算機端口兼容的適配器,這樣你就能夠將存儲卡附加到你的電腦上。在這個例子中讀卡器就是適配器。
另外一個例子是著名的電源適配器;一個三腳插頭不能鏈接到兩個電源插座,它須要使用一個電源適配器,使它與兩個電源插座兼容。
另外一個例子是一個譯者翻譯一我的對另外一我的說的話。

簡而言之segmentfault

適配器模式容許您在適配器中包裝一個不兼容的對象,使其與另外一個類兼容。

維基百科說設計模式

在軟件工程中,適配器模式是一種軟件設計模式,它容許將現有類的接口用做另外一個接口。它一般用於使現有的類與其餘類一塊兒工做,而無需修改它們的源代碼。

編程示例緩存

考慮一個遊戲,一個獵人追捕獅子。
首先我有一個獅子(Lion)的接口和全部要實現類型的獅子。安全

public interface Lion {
    void roar();
}
public class AsianLion implements Lion {
    @Override
    public void roar() {
        System.out.println("AsianLion roar");
    }
}
public class AfricanLion implements Lion {
    @Override
    public void roar() {
        System.out.println("AfricanLion roar");
    }
}

獵人但願全部捕獲的獅子都能實現Lion的接口。less

public class Hunter {
    public void hunt(Lion lion)
    {
        lion.roar();
    }
}

如今在咱們的遊戲裏增長一個"野狗"(WildDog),獵人也能夠捕獲。可是咱們不能直接這樣作,由於野狗(WildDog)有一個不一樣的接口。爲了兼容咱們的獵人(hunter),咱們必須建立一個適配器來使之兼容。ide

public class WildDog {
    public void bark()
    {
        System.out.println("WildDog bark");
    }
}
public class WildDogAdapter implements Lion {
    private WildDog wildDog;

    public WildDogAdapter(WildDog wildDog) {
        this.wildDog = wildDog;
    }

    @Override
    public void roar() {
        this.wildDog.bark();
    }
}

如今 WildDog 能夠在咱們的遊戲裏使用 WildDogAdapter來代替.

WildDog wildDog = new WildDog();
        WildDogAdapter adapter = new WildDogAdapter(wildDog);
        Hunter hunter = new Hunter();
        hunter.hunt(adapter);

?橋接模式

現實舉例

考慮你有一個不一樣頁面的網站,你應該容許用戶改變主題。你會怎麼作?爲每一個主題建立多個頁面的多個副本,或者僅僅根據用戶的喜愛建立單獨的主題並加載它們?橋式模式容許你作第二件事。

With and without the bridge pattern

簡而言之

橋式模式是傾向於選擇組合而不是繼承。實現細節從層次結構推送到另外一個具備獨立層次結構的對象。

維基百科說

橋式模式是軟件工程中使用的一種設計模式,它的意思是「將抽象與實現分離開來,以便二者可以獨立地變化」。

編程示例

翻譯一下上邊的Web頁面(Web Page)的例子。首先是獨立的WebPage層次

public interface WebPage {
    String getContent();
}
public class About implements WebPage {
    private Theme theme;

    public About(Theme theme) {
        this.theme = theme;
    }

    @Override
    public String getContent() {
        return "About page in "+theme.getColor();
    }
}
public class Career implements WebPage {
    private Theme theme;

    public Career(Theme theme) {
        this.theme = theme;
    }

    @Override
    public String getContent() {
        return "Career Page in "+theme.getColor();
    }
}

獨立的主題層次

public interface Theme {
    String getColor();
}
public class AquaTheme implements Theme {
    @Override
    public String getColor() {
        return "Light blue";
    }
}
public class DarkTheme implements Theme {
    @Override
    public String getColor() {
        return "Dark Black";
    }
}
public class LightTheme implements Theme {
    @Override
    public String getColor() {
        return "Off White";
    }
}

And both the hierarchies

Theme aquaTheme = new AquaTheme();
Theme darkTheme = new DarkTheme();

WebPage about = new About(aquaTheme);
WebPage career = new Career(aquaTheme);
System.out.println(career.getContent());
System.out.println(about.getContent());

System.out.println("");

about = new About(darkTheme);
career = new Career(darkTheme);
System.out.println(career.getContent());
System.out.println(about.getContent());

? 組合模式

現實舉例

每一個組織都是由僱員組成的。每一個僱員都有相同的特性,即有工資,有一些職責,能夠或不能夠向某人報告,能夠或不可能有一些下屬等。

簡而言之

組合模式容許客戶以統一的方式處理單個對象

維基百科說

在軟件工程中,組合模式是一個分而治之的設計模式。
組合模式描述了以相同的方式對待一組對象和單個對象。 組合的意圖是將對象「組合」到樹結構中,以表示部分整個層次結構。 實現組合模式可讓客戶端對單個對象和組合進行統一處理。

編程示例

參考咱們上邊僱員的例子。咱們又幾種類型的僱員。

public interface Employee {
    String getName();
    void setSalary(float salary);
    float getSalary();
    String getRole();
}
public class Developer implements Employee {
    private String name;
    private float salary;
    private String role;

    public Developer(String name, float salary) {
        this.name = name;
        this.salary = salary;
        this.role = "Developer";
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setSalary(float salary) {
        this.salary = salary;
    }

    @Override
    public float getSalary() {
        return this.salary;
    }

    @Override
    public String getRole() {
        return role;
    }
}
public class Designer implements Employee{
    private String name;
    private float salary;
    private String role;

    public Designer(String name, float salary) {
        this.name = name;
        this.salary = salary;
        this.role = "Designer";
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setSalary(float salary) {
        this.salary = salary;
    }

    @Override
    public float getSalary() {
        return this.salary;
    }

    @Override
    public String getRole() {
        return role;
    }
}

下面咱們用組織來保存幾種不一樣類型的僱員。

public class Orgnization {
    private List<Employee> employees = new ArrayList<>();

    public void addEmployee(Employee employee)
    {
        employees.add(employee);
    }

    public float getSalary()
    {
        float total = 0;
        for(Employee employee : employees)
        {
            total += employee.getSalary();
        }

        return total;
    }
}

使用方式以下

Employee employee1 = new Developer("John Doe",12000);
        Employee employee2 = new Designer("Jane Doe",15000);

        Orgnization orgnization = new Orgnization();
        orgnization.addEmployee(employee1);
        orgnization.addEmployee(employee2);

        System.out.println(orgnization.getSalary());

☕ 裝飾者模式

現實舉例

假設你經營一家提供多種服務的汽車服務商店。那麼如何計算要收取的費用呢?您能夠選擇一個服務並動態地將提供的服務的價格添加到最終成本。這裏的每一種服務都是裝飾者。

簡而言之

裝飾者模式容許您經過將對象在一個裝飾者類的對象中進行包裝,從而在運行時動態地更改對象的行爲。

維基百科說

在面向對象編程中,裝飾者模式是一種設計模式,它容許將行爲添加到單個對象中,不管是靜態的仍是動態的,都不會影響來自同一類的其餘對象的行爲。 裝飾者模式一般對堅持單一責任原則很是有用,由於它容許在具備獨特關注點的類之間劃分功能。

編程示例
咱們以咖啡舉例,首先咱們有一個簡單的實現了咖啡接口的咖啡。

public interface Coffee {
    float getCost();
    String getDescription();
}
public class SimpleCoffee implements Coffee {
    @Override
    public float getCost() {
        return 10;
    }

    @Override
    public String getDescription() {
        return "simpleCoffee";
    }

    @Override
    public String toString() {
        return "SimpleCoffee";
    }
}

咱們但願使代碼可擴展,以便在須要時容許選項進行修改。讓咱們作一些附加組件(decorator)

public class MilkCoffee implements Coffee {
    private Coffee coffee;

    public MilkCoffee(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public float getCost() {
        return coffee.getCost()+2;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription()+",milk";
    }
}
public class VanillaCoffee implements Coffee{

    private Coffee coffee;

    public VanillaCoffee(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public float getCost() {
        return coffee.getCost()+3;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription()+",vanilla";
    }
}
public class WhipCoffee implements Coffee {
    private Coffee coffee;

    public WhipCoffee(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public float getCost() {
        return coffee.getCost()+5;
    }

    @Override
    public String getDescription() {
        return coffee.getDescription()+",whip";
    }
}

Lets make a coffee now

Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getCost());
System.out.println(coffee.getDescription());

coffee = new MilkCoffee(coffee);
System.out.println(coffee.getCost());
System.out.println(coffee.getDescription());

coffee = new VanillaCoffee(coffee);
System.out.println(coffee.getCost());
System.out.println(coffee.getDescription());

coffee = new WhipCoffee(coffee);
System.out.println(coffee.getCost());
System.out.println(coffee.getDescription());

? 門面模式

現實舉例

你怎麼打開電腦?「按下電源鍵」你說!這就是你所相信的,由於你使用的是一個簡單的界面,電腦在外部提供,在內部它須要作不少事情來實現它。這個複雜子系統的簡單接口是一個門面。

簡而言之

門面模式提供了一個複雜子系統的簡化接口。

維基百科說

門面模式是爲更大的代碼體(如類庫)提供簡化接口的對象。

編程示例

以咱們上邊的計算機爲例。首先這是咱們的計算機。

public class Computer {
    public void getElectricShock()
    {
        System.out.println("Ouch");
    }

    public void makeSound()
    {
        System.out.println("Beep beep!");
    }

    public void showLoadingScreen()
    {
        System.out.println("Loading..");
    }

    public void bam()
    {
        System.out.println("Ready to be used!");
    }

    public void closeEverything()
    {
        System.out.println("closeEverything");
    }

    public void sooth()
    {
        System.out.println("Zzzzz");
    }

    public void pullCurrent()
    {
        System.out.println("Haaah");
    }
}

下面是門面

public class ComputerFacade {
    private Computer computer;

    public ComputerFacade(Computer computer) {
        this.computer = computer;
    }

    public void turnOn()
    {
        computer.getElectricShock();
        computer.makeSound();
        computer.showLoadingScreen();
        computer.bam();
    }

    public void turnOff()
    {
        computer.closeEverything();
        computer.pullCurrent();
        computer.sooth();
    }
}

下面開始使用門面

Computer computer = new Computer();
ComputerFacade facade = new ComputerFacade(computer);

facade.turnOn();
System.out.println("============================");
 facade.turnOff();

? 享元模式

現實舉例

你曾經在某個攤位上喝過新鮮的茶嗎?他們常常作的比你要求的多並把剩下的留給其餘顧客,以節省資源,例如氣體等。Flyweight模式就是分享。

簡而言之

它經過儘量多地與相似對象共享來最小化內存使用或計算開銷。

維基百科說

在計算機編程中,享元是一種軟件設計模式。享元是一個對象,它經過與其餘相似對象共享盡量多的數據來最小化內存使用;當一個簡單的重複表示使用不可接受的內存時,它是一種使用大量對象的方法。

編程示例
翻譯一下咱們上邊茶水的例子。首先咱們有茶的類型和茶具。

public class KarakTea {
}
public class TeaMaker {
    private Map<String,KarakTea> availableTea = new HashMap<>();

    public KarakTea make(String preference)
    {
        if(!availableTea.containsKey(preference))
        {
            availableTea.put(preference,new KarakTea());
        }

        return availableTea.get(preference);
    }
}

而後咱們有茶社來接單和提供服務。

public class TeaShop {
    private TeaMaker teaMaker;
    private Map<Integer,KarakTea> orders = new HashMap<>();

    public TeaShop(TeaMaker teaMaker) {
        this.teaMaker = teaMaker;
    }

    public void takeOrder(String teaType,int table)
    {
        orders.put(table,teaMaker.make(teaType));
    }

    public void serve()
    {
        Set<Map.Entry<Integer,KarakTea>> entrySet = orders.entrySet();
        for(Map.Entry<Integer,KarakTea> entry : entrySet)
        {
            System.out.println("Serving tea to table#"+entry.getKey());
        }
    }
}

使用方式以下

TeaMaker teaMaker = new TeaMaker();
TeaShop shop = new TeaShop(teaMaker);
shop.takeOrder("less sugar", 1);
shop.takeOrder("more milk", 2);
shop.takeOrder("without sugar", 3);

shop.serve();

? 代理模式

現實舉例

你曾經用過門禁卡嗎?打開這扇門有多種選擇,便可以使用門禁卡打開,也能夠經過按一個繞過安全按鈕的按鈕打開。 門的主要功能是打開,可是在它上面添加了一個代理來添加一些功能。讓我用下面的代碼示例更好地解釋一下。

簡而言之

使用代理模式,一個類表明另外一個類的功能。

維基百科說

代理,在其最通常的形式中,是一個類做爲其餘東西的接口。 代理是由客戶端調用的包裝器或代理對象,以訪問實際服務對象。代理的使用能夠簡單地轉發到實際對象,或者提供額外的邏輯。 在代理的額外功能中能夠提供,例如在實際對象上的操做是資源密集型的緩存,或者在調用實際對象的操做以前檢查先決條件。

編程示例

上邊安全門的例子。首先咱們有一個門的接口和一個門的實現

public interface Door {
    void open();
    void close();
}
public class LabDoor implements Door {
    @Override
    public void open() {
        System.out.println("Opening lab door");
    }

    @Override
    public void close() {
        System.out.println("Closing lab door");
    }
}

咱們有一個來從來確保咱們想要的門。

public class SecuredDoor {
    private Door door;

    public SecuredDoor(Door door) {
        this.door = door;
    }

    public void open(String pwd) {
        if(authenticate(pwd))
        {
            door.open();
        }
        else
        {
            System.out.println("Big no! It ain't possible.");
        }
    }

    private boolean authenticate(String pwd)
    {
        return "$ecr@t".equals(pwd);
    }

    public void close() {
        this.door.close();
    }
}

使用方式以下

Door door = new LabDoor();
SecuredDoor securedDoor = new SecuredDoor(door);

securedDoor.open("invalid");
//securedDoor.open("$ecr@t");

還有一個例子是某種形式的數據映射的實現。例如我最近編寫了一個MongoDB的ODM。在mongodb周圍利用魔術方法__call()我寫了一個代理,全部的方法調用都被代理到原生的mongo類。檢索的結果做爲返回的數據,若是find或者findOne的數據映射到某個類,這類將會代替cursor返回。

相關文章
相關標籤/搜索