设计模式--简化解释(二)——结构型设计模式

cnsuifeng

1.创建型设计模式
2.结构型设计模式
3.行为型设计模式

结构型设计模式

简而言之

结构模式主要涉及对象的组成,或者是实体如何相互使用。或者,另一个解释是,他们帮助回答“如何构建一个软件组件?”

维基百科说

在软件工程中,结构设计模式是通过识别实体之间关系的简单方法来简化设计的设计模式。

?适配器模式

现实举例

考虑一下你的记忆卡上有一些图片,你需要把它们转移到你的电脑上。为了转移它们,你需要某种与你的计算机端口兼容的适配器,这样你就可以将存储卡附加到你的电脑上。在这个例子中读卡器就是适配器。
另一个例子是著名的电源适配器;一个三脚插头不能连接到两个电源插座,它需要使用一个电源适配器,使它与两个电源插座兼容。
另一个例子是一个译者翻译一个人对另一个人说的话。

简而言之

适配器模式允许您在适配器中包装一个不兼容的对象,使其与另一个类兼容。

维基百科说

在软件工程中,适配器模式是一种软件设计模式,它允许将现有类的接口用作另一个接口。它通常用于使现有的类与其他类一起工作,而无需修改它们的源代码。

编程示例

考虑一个游戏,一个猎人追捕狮子。
首先我有一个狮子(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的接口。

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

现在在我们的游戏里增加一个"野狗"(WildDog),猎人也可以捕获。但是我们不能直接这样做,因为野狗(WildDog)有一个不同的接口。为了兼容我们的猎人(hunter),我们必须创建一个适配器来使之兼容。

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返回。

阅读 1.4k

JAVA进阶
JAVA,设计模式,多线程,Spring

为了梦,坚守

24 声望
1 粉丝
0 条评论
你知道吗?

为了梦,坚守

24 声望
1 粉丝
宣传栏