设计模式【三】结构型模式(设计对象以满足特定的项目约束)

1、装饰者模式(Decorator Pattern)

1.1 装饰者模式定义
装饰者模式:可以动态的给对象添加一些职责,就增加的功能来说,它比生成子类的方式更加灵活。
1.2装饰者模式类图
装饰者模式类图
Component是一个对象的接口,可以动态的给其添加一些职责。ConcreteComponentComponent的一个具体的实现类,也可以给其动态的添加一些额外的职责。Decorator是装饰器,从外部类对Component进行一些扩展。ConcreteDecorator是具体的装饰对象,起到给Component添加功能的作用。
1.3实例及代码
实例:变形金刚(变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔)
类图如下:变形金刚类图
代码:

//变形金刚的接口
public interface Transform {
    void movie();
}
//car实现了该接口
public class Car implements Transform {
    @Override
    public void movie(){
        System.out.println("I can movie in the land");
    }
}
//变形金刚的包装类
public class Changer implements Transform{
    protected Transform transform;
    protected void setTransform(Transform transform) {
        this.transform = transform;
    }
    @Override
    public void movie(){
        if(transform!=null){
            transform.movie();
        }
    }
}
//变形成机器人,这是具体的装饰对象
public class Robot extends Changer{
    public void say(){
        super.movie();
        System.out.println("I am a robot,I can say talk with people");

    }
}
//变形成飞机,这是具体的装饰对象
public class Airplan extends Changer{
    public void fly(){
        super.movie();
        System.out.println("I am an airplan,I can fly in the sky");
    }
}

2、代理模式(Proxy Pattern)

2.1 代理模式定义
代理模式:给一个对象提供一个代理,由代理来控制原对象的引用。
2.2 代理模式类图
代理模式类图
Subject定义了ProxyRealSubject的公用接口,这就使得在使用RealSubject的地方来使用Proxy进行替代
2.3 代码实现
实例:小明和小张是舍友,某天早上在他们要去上课前,小张突然感觉肚子不舒服就去校医院。在去校医院之前他让小明帮他交作业。此时小明就是一个代理,他代替小张执行了交作业这个动作。
代码:

public interface Person {
    void giveHousework();
}
//同学小张
public class Student implements Person {
    public String name;
    public Student(String name){
        this.name = name;
    }
    @Override
    public void giveHousework() {
        System.out.println(name+" : 交了50元");
    }
}
//这个是代理,帮小张交作业
public class StudentProxy implements Person{
    private Student student;

    public void setStudent(Student student) {
        this.student = student;
    }
    @Override
    public void giveHousework() {
        student.giveHousework();
    }
}

3、外观模式(Facade Pattern)

3.1 外观模式定义
外观模式:外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
3.2 外观模式类图
外观模式类图
这里Facade是外观角色,而SubSystem是子系统角色。
3.3 外观模式实例及代码
我们的每一个电脑都会有CPU、硬盘(disk)和内存(memory)。在每次我们启动或者关闭电脑时,这三个相应的部件也会相应的启动或者关闭。我们若在客户端直接调用这三个部件,则会造成客户端与这三个子部件的耦合度过高。所以我们应该使用外观模式,让客户端直接与外观角色进行通信来减低耦合。这也是典型的迪米特法则的一个应用。

public class CPU {
    public void  start(){
        System.out.println("CPU is starting");
    }
    public void shutdown(){
        System.out.println("CPU is shutdowning");
    }
}
//这里Memory和Disk两个类省略,具体代码可参考我的github
public class Facade {
    private CPU cpu;
    private Disk disk;
    private Memory memory;
    public Facade() {
        cpu = new CPU();
        disk = new Disk();
        memory = new Memory();
    }
    public void start() {
        System.out.println("Computer is starting,other component will start");
        cpu.start();;
        disk.start();
        memory.start();
        System.out.println("Computer started");
    }
    public void shutdown() {
        System.out.println("Computer is shutDowning,other component will shutdown");
        cpu.shutdown();
        disk.shutdown();
        memory.shutdown();
        System.out.println("Computer shutDown");
    }
}

3.3 外观模式优缺点
-对客户端屏蔽了子系统的组件;为客户端提供了统一访问的接口,这并不会影响客户端对子组件的直接调用;实现了子部件与客户端的解耦和,子组件的变化不会影响到调用它的客户端只需要修改相应的外观角色即可。
-不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性

4、适配器模式(Adapter Pattern)

4.1 适配器模式定义
适配器模式:将一个接口转换成客户希望的另一个接口,适配器模式使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式的动机是让由于接口不兼容而不能交互的类可以一起工作。适配器又可以分为类适配器和对象适配器,其中类适配器是通过继承来实现的(即继承适配者类);而对象适配器是通过关联来完成的。
4.2 适配器模式结构图(这里特指对象适配器)
适配器模式类图
Client是客户端,Target目标抽象类,Adapter适配器类,Adaptee适配者类
4.3 适配器模式实例及代码
在我们生活中使用的苹果手机,它的额定充电电压是5.1v,而我们国家的标准电压是220v。所以需要电源适配器来将220v转换为5.1v。这就是一个适配器模式,将原本不兼容的220v转换为苹果手机所需要的5.1v。这里5.1v是目标的抽象类,而Adaptee是适配者类。

public interface VoltageFive { //目标抽象类
    void outputFiveV();
}
//适配者类
public class Voltage220V {
    public void output220v(){
        System.out.println("输出220v电压");
    }
}
//适配器类
public class VoltageAdapter implements VoltageFive {
    private Voltage220V voltage220V;
    public VoltageAdapter(){
        new Voltage220V();
    }
    @Override
    public void outputFiveV() {
        System.out.println("源电压是220v,已经转换成5.1v电压");
    }
}

4.4 适配器模式优缺点
-一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口;将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
-与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

5、组合模式(Composite Pattern)

5.1组合模式定义
组合模式:又称为部分-整体模式,将对象组合成树状的层次结构的模式,使得用户对单个对象和组合对象具有一致性的访问特点。
5.2 组合模式结构图
组合模式结构图
Component是抽象组件,用来声明树叶组件和树枝组件的公开接口。Leaf是树叶组件,没有子节点,用于实现抽象构件角色中 声明的公共接口。Composite是树枝组件,有子节点,它实现了抽象构件角色中声明的接口,它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
在组合模式中包含了透明式的组合模式和安全式的组合模式。在透明式中:抽象组件声明了所有的子类方法,所以客户端不用去分辨对象是叶子节点还是子节点。但是叶子节点没有Add()、Remove()、GetChild() 等方法,会抛出异常或者单独处理。在安全式中:将管理构建的方法移到树枝组件中,而抽象组件和树叶组件不含有管理方法。在客户端调用时需要区分是叶子节点还是树枝节点。
5.3 组合模式实例及代码
假如我们要访问下列图中叶子节点{leaf1,leaf2,leaf3}这三个节点:
在这里插入图片描述

//这是使用了安全式的组合模式
public interface Component {
    void operation();
}
public class Leaf implements Component {
    private String name;
    public Leaf(String name){
        this.name = name;
    }
    @Override
    public void operation() {
        System.out.println("叶子节点"+name+":被访问");
    }
}
public class Composite implements Component {
    private List<Component> componentsList = new ArrayList<>();
    public void add(Component component) {
        componentsList.add(component);
    }
    public void remove(Component component) {
        componentsList.remove(component);
    }
    public Component getChildComponent(int i) {
        return componentsList.get(i);
    }
    @Override
    public void operation() {
        for (Component component : componentsList) {
            component.operation();
        }
    }
}

5.4 组合模式优缺点
-使得客户端无需关心是单个对象还是组合对象,可以对它们进行统一的处理;可以随时在组合体内加入新的对象而不更改源代码,符合“开闭”原则
-设计较为复杂,需要客户端梳理类的层次结构关系;不能够对组合体内的对象的数量加以限制

6、桥接模式(Bridge Pattern)

6.1桥接模式定义
桥接模式:将抽象部分与实现部分相分离,使它们都可以独立的变化。这并不是简单的将抽象类与实现类分离,而是抽象类和派生类用来实现自己的对象。换句话说:实现系统可能有多个角度分类,每一种角度都可能变化,那么把这种多角度分类给分离出来让他们独立变化,减少他们之间耦合。
6.2桥接模式结构图
桥接模式结构图
6.3桥接模式实例及代码
我们现在生产了不同型号的小汽车,M型、N型和P型,现在需要使用红、黄、蓝三种颜色对这三种型号的小汽车进行上色。我们可以根据实际需要来将不同型号的汽车与颜色进行组合。这样系统的可扩展性得到了很大的提升。

public abstract class Automobile {
    protected Color color;
    public void setColor(Color color){
        this.color = color;
    }
    public abstract void paintColor();
}
public interface Color {
    void color(String type);
}
//N型和P型省略,见github
public class TypeM extends Automobile {
    @Override
    public void paintColor() {
        color.color("M型");
    }
}
//其他两种颜色省略,具体见我github
public class Yellow implements Color {
    @Override
    public void color(String type) {
        System.out.println("黄色的"+type);
    }
}

6.4桥接模式优缺点
-分离了抽象接口和实现部分;系统的扩展性比较好,扩展系统的任意一个维度都不需要修改原来的代码
-增加了系统的复杂性,一般聚合关系发生在抽象层,需要人们面向抽象编程;将两个维度抽象出来有一定的困难

7、中介者模式(Mediator Pattern)

7.1中介者模式定义
中介者模式:用一个中介对象来封装一系列对象的交互。中介者使得各对象不需要显式的相互调用,从而其耦合松散,而且可以独立的改变它们之间的交互。中介者模式使得原来我们各个对象之间相互通信组成的复杂网络转换为一个星型的网络结构,简化了网络的模型。它同时也是一个迪米特法则的一个典型应用。
7.2中介者模式结构图
中介者模式结构图
7.3中介者模式实例及代码
我们在生活中常常听到的联合国就是一个中介者模式的应用,联合国的各个成员国可以通过联合国这个组织来协调它们之间的争端和矛盾。我们现在假设,中国和巴基斯坦要进行一项友好的协商一件事,这我们可以通过中介者模式来实现。

public interface Mediator {
    void add(Station station);
    void send(String message,Station country);
}
public class ConcreteMediator implements Mediator {
    private List<Station> countries = new ArrayList<Station>();
    public ConcreteMediator() {
        super();
    }
    @Override
    public void add(Station station) {
        if (!countries.contains(station)) {
            countries.add(station);
        } else {
            System.out.println("您已经加入过联合国了");
        }
    }
    @Override
    public void send(String message, Station country) {
        for (Station station : countries) {
            if (!country.equals(station)) {
                country.receive();
            }
        }
    }
}
public interface Station {
    void send(String message);
    void receive();
}
public class China implements  Station {
    private Mediator mediator;
    public China(Mediator mediator) {
        this.mediator = mediator;
    }
    @Override
    public void send(String message) {
        mediator.send(message+"我想和你商谈大事,你可愿意?",this);
    }
    @Override
    public void receive() {
        System.out.println("中国已经收到消息");
    }
}

7.4中介者模式优缺点
-通过中介将网络构建为一个星型的网络,降低了网络的复杂度;降低了对象之间的耦合性,使得对象易于独立地被复用
-过度依赖于中介,一旦中介过于复杂且出现问题时会造成系统的崩溃。这违背了“单一职责”的原则

8、享元模式(Flyweight Pattern)

8.1享元模式定义
享元模式:运用共享技术有效地支持大量细粒度对象的复用。在享元模式中共享的享元对象是内部状态,而外部状态通过环境来设置。一般来说共享的内部状态是有限的,其一般设计为较小的对象这种对象也称为细粒度对象。享元模式是为了减少相同的实例化对象的数量,提高了系统资源的使用率。
8.2享元模式结构图
享元模式结构图
8.3享元模式实例及代码
假设我们现在有一个5*5的棋盘来下棋,棋盘上有两种颜色的棋子黑色和白色。我们现在需要在客人下棋时将对应的棋子的颜色以及位置坐标打印出来。若为棋盘上的每一个棋子都实例化一个棋子,每个棋子的的生成都会消耗系统得分资源,这时就需要享元模式只为每一种黑色和白色棋子生成一个实例将这个实例进行共享,而此时棋子的位置坐标就是一个外部状态需要在使用棋子时外部传入。

public interface ChessPieces {
    void downPieced(Location location);
}
public class WhitePieces implements  ChessPieces{
    @Override
    public void downPieced(Location location) {
        System.out.println("白色棋子的横坐标是:"+location.getPosition_x()+
                "纵坐标是:"+location.getPosition_y());
    }
}
public class BlackPieces implements ChessPieces {
    @Override
    public void downPieced(Location location) {
        System.out.println("黑色棋子的横坐标是:"+location.getPosition_x()+
                "纵坐标是:"+location.getPosition_y());
    }
}
public class PiecesFactory {
    public static Map<String,ChessPieces> pieces = new HashMap<String,ChessPieces>();
    static{
        ChessPieces whitePieces = new WhitePieces();
        pieces.put("whitePieces",whitePieces);
        ChessPieces blackPieces = new BlackPieces();
        pieces.put("blackPieces",blackPieces);
    }
    public static ChessPieces getPiecesByType(String type){
        if("whitePieces".equalsIgnoreCase(type)){
            return pieces.get("whitePieces");
        }else if("blackPieces".equalsIgnoreCase(type)){
            return pieces.get("blackPieces");
        }else{
            return null;
        }
    }
    public static int getCount(){
        return pieces.size();
    }
}
public class Location {
    private int position_x;
    private int position_y;
    public Location(int position_x,int position_y){
        this.position_x = position_x;
        this.position_y = position_y;
    }
    public int getPosition_x() {
        return position_x;
    }
    public void setPosition_x(int position_x) {
        this.position_x = position_x;
    }
    public int getPosition_y() {
        return position_y;
    }
    public void setPosition_y(int position_y) {
        this.position_y = position_y;
    }
}

8.4享元模式的优缺点
-享元模式外部状态较为独立且不影响内部状态,故可以在不同的场景中使用;由于享元模式共享了系统的核心代码,相同的对象是保留了一份,降低了系统的压力。
-为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性;读取享元模式的外部状态会使得运行时间稍微变长。
参考:
1、图解设计模式
2、《大话设计模式》
3、代码参考
4、参考博客 https://www.cnblogs.com/lthIU/p/5860607.html , http://c.biancheng.net/view/1373.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值