简单了解设计模式(结构型模式)(2)

本文详细介绍了结构型模式中的装饰模式、组合模式、适配器模式、外观模式和代理模式,展示了它们在软件设计中的应用场景和优点,帮助理解如何在实际项目中灵活运用这些设计模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

结构型模式:结构型模式涉及如何组合类和对象以形成更大的结构,和类有关的结构型模式涉及如何合理和使用继承机制,和对象有关的结构型模式涉及如何合理地使用对象组合机制。

(一)装饰模式

装饰模式:动态地给对象添加一些额外的职责。就功能来说,装饰模式相比生成子类更为灵活。

装饰模式的结构中包括4种角色:

  • 抽象组件(Component):抽象组件是一个抽象类。抽象组件定义了“被装饰者”需要进行“装饰”的方法。
  • 具体组件(ConcreteComponent):具体组件是抽象组件的一个子类,具体组件的实例称为“被装饰者”。
  • 装饰(Decorator):装饰也是抽象组件的一个子类,但装饰还包含一个抽象组件声明的变量以保存“被装饰者“的引用。装饰可以是一个抽象类也可以是一个非抽象类,如果是非抽象类,该类的实例称为“装饰者”。
  • 具体装饰(ConcreteDecorator):具体装饰是装饰的一个非抽象子类,具体装饰的实例称为“装饰者”。

装饰模式的类图:
装饰模式的类图

例子:

假设系统中有一个Bird抽象类以及Bird类的一个子类Sparrow。Sparrow类实现了Bird类fly方法,使得Sparrow类创建的对象(麻雀)调用fly()方法能连续飞行100米。
现在,用户需要两只鸟,无论是那种鸟都可以,但必须分别能连续飞行150米和200米。显然,现有的系统无法向用户提供这样的Bird对象,所以需要修改现有的系统。此处,使用装饰者模式,即可实现不必修改系统的代码,只需在系统中添加“装饰”,该系统将可以创建出用户需要的鸟。

抽象组件

public abstract class Bird {
    public abstract int fly();
}

具体组件

public class Sparrow extends Bird{
    @Override
    public int fly() {
        return 100;
    }
}

装饰(Decorator)

public abstract class Decorator extends Bird{
    Bird bird;  //  被装饰者
    public Decorator(){

    }
    public Decorator(Bird bird){
        this.bird = bird;
    }
    public abstract int eleFly(); //用于装饰fly()的方法,行为由具体装饰者去实现
}

具体装饰

public class SparrowDecorator extends Decorator{
    SparrowDecorator(Bird bird){
        super(bird);
    }
    @Override
    public int fly() {                  //被装饰方法
        return bird.fly() + eleFly();   //让装饰者先调用fly(),然后再调用eleFly()
    }

    @Override
    public int eleFly() {
        return 50;                      //装饰者新添加的方法
    }
}

Application

public class Application {
    public static void main(String[] args) {
        Bird bird = new Sparrow();
        System.out.println(bird.fly());
        bird = new SparrowDecorator(bird);
        System.out.println(bird.fly());
        bird = new SparrowDecorator(bird);
        System.out.println(bird.fly());
        bird = new SparrowDecorator(bird);
        System.out.println(bird.fly());
        bird = new SparrowDecorator(bird);
        System.out.println(bird.fly());
    }
}

运行结果效果图:
运行结果图

1.装饰模式的优点

(1)被装饰者和装饰者是松耦合关系。由于装饰仅仅依赖于抽象组件,因此具体装饰只知道它要装饰的对象是抽象组件某一个子类的实例,但不需要知道是哪一个具体子类。

(2)装饰模式满足“开-闭原则”。不必修改具体组件,就可以增加新的针对该具体组件的具体装饰。

(3)可以使用多个具体装饰来装饰具体组件组件的实例。

2.适合使用装饰模式的情景

(1)程序希望动态地增强类的某个对象的功能,而又不影响到该类的其他对象。

(2)采用继承来增强对象功能不利于系统的扩展和维护。


(二)组合模式

组合模式:将对象合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

组合模式包含有以下三种角色:

  • 抽象组件(Component):是一个接口(抽象类),该接口(抽象类)定义了个体对象和组合对象需要实现的关于操作其子节点的方法,比如add()、remove()以及getChild()等方法。抽象组件也可以定义个体对象和组合对象用于操作其自身的方法,比如isLeaf()。
  • Composite节点(Composite Node):实现Component接口(或者抽象类)类的实例,Composite节点不仅实现Component接口,而且可以含有其他Composite节点或Leaf节点的引用。
  • Leaf节点(Leaf Node):实现Component接口(或抽象类)类的实例,Leaf节点实现Component接口,不可以含有其他Composite节点或Leaf节点的引用,因此,叶节点在实现Component接口有关操作子节点的方法时,比如add()、remove()和getChild()方法,可让方法抛出一个异常,也可以实现为空操作。

组合模式的类图:
组合模式的类图

例子:

一个连队由一个连长,2个排长,6个班长和60个士兵所构成,一共69人。连长直接指挥2个排长,每个排长直接指挥3个班长,每个班长直接指挥10个士兵。连长的军饷是每月5000元,排长是4000元,班长是2000元,士兵是1000元。现在使用组合模式,让连队的军士形成树形结构,并计算了一个班的军饷、一个排的军饷和整个连队的军饷。

抽象组件(Component)

public interface MilitaryPerson {
    public void add(MilitaryPerson person);
    public void remove(MilitaryPerson person);
    public MilitaryPerson getChild(int index);
    public Iterator<MilitaryPerson> getAllChildren();
    public boolean isLeaf();
    public double getSalary();
    public void setSalary(double salary);
}

Composite 结点

public class MilitaryOfficer implements MilitaryPerson {
    LinkedList<MilitaryPerson> list;
    String name;
    double salary;
    MilitaryOfficer(String name,double salary){
        this.name = name;
        this.salary = salary;
        list = new LinkedList<MilitaryPerson>();
    }
    @Override
    public void add(MilitaryPerson person) {
        list.add(person);
    }

    @Override
    public void remove(MilitaryPerson person) {
        list.remove(person);
    }

    @Override
    public MilitaryPerson getChild(int index) {
        return list.get(index);
    }

    @Override
    public Iterator<MilitaryPerson> getAllChildren() {
        return list.iterator();
    }

    @Override
    public boolean isLeaf() {
        return false;
    }

    @Override
    public double getSalary() {
        return salary;
    }

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

Leaf 结点

public class MilitarySoldier implements MilitaryPerson{
    double salary;
    String name;
    MilitarySoldier(String name,double salary){
        this.name = name;
        this.salary = salary;
    }
    @Override
    public void add(MilitaryPerson person) { }

    @Override
    public void remove(MilitaryPerson person) { }

    @Override
    public MilitaryPerson getChild(int index) {
        return null;
    }

    @Override
    public Iterator<MilitaryPerson> getAllChildren() {
        return null;
    }

    @Override
    public boolean isLeaf() {
        return true;
    }

    @Override
    public double getSalary() {
        return salary;
    }

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

Application

public class Application {
    public static void main(String[] args) {
        MilitaryPerson 连长 = new MilitaryOfficer("连长", 5000);
        MilitaryPerson 排长1 = new MilitaryOfficer("一排长", 4000);
        MilitaryPerson 排长2 = new MilitaryOfficer("二排长", 4000);
        MilitaryPerson 班长11 = new MilitaryOfficer("一班长", 2000);
        MilitaryPerson 班长12 = new MilitaryOfficer("二班长", 2000);
        MilitaryPerson 班长13 = new MilitaryOfficer("三班长", 2000);
        MilitaryPerson 班长21 = new MilitaryOfficer("一班长", 2000);
        MilitaryPerson 班长22 = new MilitaryOfficer("二班长", 2000);
        MilitaryPerson 班长23 = new MilitaryOfficer("三班长", 2000);
        MilitaryPerson[] 士兵 = new MilitarySoldier[60];
        for (int i = 0; i < 士兵.length; i++)
            士兵[i] = new MilitarySoldier("小兵", 1000);
        连长.add(排长1);
        连长.add(排长2);
        排长1.add(班长11);
        排长1.add(班长12);
        排长1.add(班长13);
        排长2.add(班长21);
        排长2.add(班长22);
        排长2.add(班长23);
        for (int i = 0; i <= 9; i++) {
            班长11.add(士兵[i]);
            班长12.add(士兵[i + 10]);
            班长13.add(士兵[i + 20]);
            班长21.add(士兵[i + 30]);
            班长22.add(士兵[i + 40]);
            班长23.add(士兵[i + 50]);
        }
        System.out.println("一排的军饷:" + computerSalary(排长1));
        System.out.println("一班的军饷:" + computerSalary(班长11));
        System.out.println("全连的军饷:" + computerSalary(连长));
    }

    public static double computerSalary(MilitaryPerson person) {
        double sum = 0;
        if (person.isLeaf() == true)
            sum = sum + person.getSalary();
        if (person.isLeaf() == false) {
            sum = sum + person.getSalary();
            Iterator<MilitaryPerson> iterator = person.getAllChildren();
            while (iterator.hasNext()) {
                MilitaryPerson p = iterator.next();
                sum = sum + computerSalary(p);
            }
        }
        return sum;
    }
}

运行结果效果图
运行结果

1.组合模式的优点

(1)组合模式中包含个体对象和组合对象,并形成树形结构,使用户可以方便地处理个体对象和组合对象。

(2)组合对象和个体对象实现了相同的接口,用户一般无须区分个体对象和组合对象。

(3)当增加新的Composite节点和Leaf节点时,用户的重要代码不需要做出修改。

2.适合使用组合模式的情景

(1)当想表示对象的部分-整体层次结构。

(2)希望用户用一致的方式处理个体对象和组合对象。


(三)适配器模式

适配器模式(别名:包装器):将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适配器模式的结构中包含3种角色:

  • 目标(Target):目标是一个接口,该接口是客户想使用的接口。
  • 被适配者(Adaptee):被适配者是一个已存在的接口或者抽象类,这个接口或者抽象类需要适配。
  • 适配器(Adapter):适配器是一个类,该类实现了目标接口并包含有被适配者的应用,即适配器的职责是对被适配者接口与目标接口进行适配。

适配器模式的类图:
适配器类图

例子:

现在用户已有一个两相的插座,但最近用户又有了一个新的三相插座。用户现有一台洗衣机和一台录音机,洗衣机按着三相插座的标准配有三相插头,而录音机按着两相插座的标准配有两相插头,现在用户想用新的三相插座来使用洗衣机和录音机,即用新的三相插座为洗衣机和录音机接通电流。

目标(Target)

public interface DirectCurrent {
    public String giveDirectCurrent();
}

被适配者

public interface AlternateCurrent {
    public String giveDirectCurrent();
}

适配器

public class ElectricAdapter implements DirectCurrent{
    AlternateCurrent out;
    ElectricAdapter(AlternateCurrent out){
        this.out = out;
    }
    @Override
    public String giveDirectCurrent() {
        String m = out.giveDirectCurrent();  //先由out得到交流电
        StringBuffer str = new StringBuffer(m);
        //将交流转换为直流
        for(int i = 0; i < str.length();i++){
            if(str.charAt(i) == '0'){
                str.setCharAt(i,'1');
            }
        }
        m = new String(str);
        return m;                //返回直流电
    }
}

Application

public class Application {
    public static void main(String[] args) {
        AlternateCurrent aElectric = new PowerCompany();//交流电
        Wash wash = new Wash();
        wash.turnOn(aElectric);                         //洗衣机使用交流电
        //对交流电aElectric进行适配得到直流电dElectric
        DirectCurrent dElectric = new ElectricAdapter(aElectric);

        Recorder recorder = new Recorder();
        recorder.turnOn(dElectric);                     //录音机使用直流电

    }
}
//交流电提供者
class PowerCompany implements AlternateCurrent{

    @Override
    public String giveDirectCurrent() {
        return "101010101010101010101010";//表示交流电
    }
}
//洗衣机使用交流电
class Wash{
    public void turnOn(AlternateCurrent a){
        System.out.println("洗衣机使用交流电:" + a.giveDirectCurrent());
    }
}
//录音机使用交流电
class Recorder{
    public void turnOn(DirectCurrent a){
        System.out.println("录音机使用直流电:" + a.giveDirectCurrent());
    }
}

运行效果图
运行效果图

1.适配器模式的优点

(1)目标和被适配者是完全解耦关系。

(2)适配器模式满足“开-闭原则”。当添加一个实现Adaptee接口的新类时,不必修改Adapter,Adapter就能对这个新类的实例进行适配。

2.适合使适配器模式的情景

(1)一个程序想使用已经存在的类,但该类所实现的接口和当前程序所使用的接口不一致。


(四)外观模式

外观模式:为系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式的结构中包括两个角色:

  • 子系统(Subsystem):子系统是若干个类的集合。这些类的实例协同合作为用户提供所需要的功能,子系统中任何类都不包含外观类的实例引用。
  • 外观(Facade):外观是一个类,该类包含子系统中全部或者部分类的实例引用,当用户想要和子系统中的若干个类的实例打交道时,可以代替地和子系统的外观类的实例打交道。

外观模式的类图:
外观模式的类图

例子:

一个用户想要在报纸上刊登广告,那么用户只需要将广告的内容交给子系统的外观实例即可,外观的实例将负责和子系统中的类的实例进行交互完成用户所指派的任务。

子系统

public class CheckWord {
    public final int basicAmount = 85;
    String advertisement;
    int amount;
    public CheckWord(String advertisement){
        this.advertisement = advertisement;
    }
    public void setChargeAmount(){
        amount = advertisement.length() + basicAmount;  //计算出计费字符数目
    }
    public int getAmount(){
        return amount;
    }
}
public class Charge {
    public final int basicCharge = 12;
    CheckWord checkword;
    Charge(CheckWord checkword){
        this.checkword = checkword;
    }
    public void giveCharge(){
        int charge = checkword.getAmount()*basicCharge;
        System.out.println("广告费用:"+charge+"元");
    }
}
public class TypeSeting {
    String advertisement;
    public TypeSeting(String advertisement){
        this.advertisement = advertisement;
    }
    public void typeSeting(){
        System.out.println("广告排版格式:");
        System.out.println("*********");
        System.out.println(advertisement);
        System.out.println("*********");
    }
}

外观

public class ClientServerFacade {
    private CheckWord checkWord;
    private Charge charge;
    private TypeSeting typeSeting;
    String advertisment;
    public ClientServerFacade(String advertisment){
        this.advertisment = advertisment;
        checkWord = new CheckWord(advertisment);
        charge = new Charge(checkWord);
        typeSeting = new TypeSeting(advertisment);
    }
    public void doAdvertisement(){
        checkWord.setChargeAmount();
        charge.giveCharge();
        typeSeting.typeSeting();
    }
}

Application

public class Application {
    public static void main(String[] args) {
        ClientServerFacade clientFacade;
        String clientAdvertisement = "月光电脑,价格6356元,联系电话:2345123";
        clientFacade = new ClientServerFacade(clientAdvertisement);
        clientFacade.doAdvertisement();
    }
}

运行效果图
运行效果图

1.外观模式的优点

(1)使客户和子系统中的类无耦合,并且是子系统使用起来更加方便。

(2)外观只是提供了一个更加简洁的界面,并不影响用户直接使用子系统中的类。

(3)子系统中任何类对其方法的内容进行修改,不影响外观类的代码。

2.适合使用外观模式的情景

(1)对于一个复杂的子系统,需要为用户提供一个简单的交互操作。

(2)不希望客户代码和子系统中的类有耦合,以便提高子系统的独立性和可移植性。

(3)当整个系统需要构建一个层次结构的子系。


(五)代理模式

代理模式:为其他对象提供一种代理以控制对这个对象的访问

代理模式包括3种角色:

  • 抽象主题(Subject):抽象主题是一个接口,该接口是对象和它的代理所共用的接口,即是RealSubject角色和Proxy角色实例所实现的接口。

  • 实际主题(RealSubject):实际主题是实现抽象主题接口的类。实际主题的实例是代理角色(Proxy)实例所要代理的对象。

  • 代理(Proxy):代理是实现抽象主题接口的类(代理和实际主题实现了相同的接口)。代理含有主题接口声明的变量,该变量用来存放RealSubject角色的实例引用,这样一来,代理的实例就可以控制对它所包含的RealSubject角色的实例访问,即可以控制对它所代理对象的访问。

代理模式的类图:
代理模式的类图

例子:

公司里的秘书是老板的代理,老板和秘书都有听电话的方法:herePhone()。公司要求用户必须首先和秘书通电话才能和老板通电话,也就是说,用户必须首先请求秘书调用herePhone(),当秘书确认老板可以接电话时,就将用户的实际请求委派给老板,即让老板调用herePhone()方法。

抽象主题(Subject)

public interface Employee {
    public String hearPhone(String s);
}

实际主题(RealSubject)

public class Boss implements Employee{
    @Override
    public String hearPhone(String s) {
        if(s.contains("买") || s.contains("卖")){
            return "好的,等以后约个时间面谈";
        }else if(s.contains("吃饭")){
            return "好的,按时来";
        }else
            return "以后再联系吧";
    }
}

代理(Proxy)

public class Secretary implements Employee{
    Boss boss;
    Secretary(){
        boss = new Boss();
    }
    @Override
    public String hearPhone(String s) {
        if(!(s.contains("恐吓") || s.contains("脏话"))){
            String back = boss.hearPhone(s);
            return "我们老板说:" + back;
        }
        return "我们老板说:不接你的电话";
    }
}

Application

public class Application {
    public static void main(String[] args) {
        Secretary secretary = new Secretary();
        String back = secretary.hearPhone("你好,我要买贵公司产品");
        System.out.println(back);
        back = secretary.hearPhone("我是老朋友,请你吃饭");
        System.out.println(back);
        back = secretary.hearPhone("聊天");
        System.out.println(back);
        back = secretary.hearPhone("恐吓老板");
        System.out.println(back);
    }
}

运行效果图
在这里插入图片描述

1.代理模式的优点

(1)代理模式可以屏蔽用户真正请求的对象,使用户程序和真正的对象之间解耦。

(2)使用代理来担当那些创建耗时的对象的替身。

2.适合使用代理模式的情景

(1)程序可能不希望用户直接访问该对象,而是提供一个特殊的对象以控制对当前对象的访问。

(2)如果一个对象(例如很大的图像)需要很长时间才能加载完成。

(3)如果对象位于远程主机上,需要为用户提供访问该远程对象的能力。


(六) 享元模式

享元模式:运用共享技术有效地支持大量细粒度的对象。

享元模式包括3种角色:

  • 享元接口(Flyweight):是一个接口,该接口定义了享元对外公开其内部数据的方法,以及享元接收外部数据的方法。

  • 具体享元(Concrete Flyweight):实现享元接口的类,该类的实例称为享元对象,或简称享元。具体享元类的成员变量为享元对象的内部状态,享元对象的内部状态必须与所处的周围环境无关,即要保证使用享元对象的应用程序无法更改享元的内部状态,只有这样才能使享元对象在系统中被共享。因为享元对象是用来共享的,所以不能允许用户各自地使用具体享元类来创建对象,这样就无法达到共享的目的,因为不同用户用具体享元类创建的对象显然是不同的,所以,具体享元类的构造方法必须是private的,其目的是不允许用户程序直接使用具体享元类来创建享元对象,创建和管理享元对象有享元工厂负责。

  • 享元工厂(Flyweight Factory):享元工厂是一个类,该类的实例负责创建和管理享元对象,用户或其他对象必须请求享元工厂为它得到一个享元对象。享元工厂可以通过一个散列表(也称作共享池)来管理享元对象,当用户程序或其他若干个对象向享元工厂请求一个享元对象时,如果享元工厂的散列表中已有这样的享元对象,享元工厂就提供这个享元对象给请求者,否则就创建一个享元对象添加到散列表中,同时将该享元对象提供给请求者。显然,当若干个用于或对象请求享元工厂提供一个享元对象时,第一个用户获得该享元对象的时间可能慢一些,但是后继的用户会较快地获得这个享元对象。可以使用单例模式来设计这个享元工厂,即让系统中只有一个享元工厂的实例,另外,为了让享元工厂能生成享元对象,需要将具体享元类作为享元工厂的内部类。

享元模式的类图:
享元模式

例子:

创建若干个“奥迪A6”轿车和若干个“奥迪A4”轿车。“奥迪A6”轿车长、宽、高都是相同的,颜色和功率可以不同;“奥迪A4”轿车的长、宽和高都是相同的,颜色和功率可以不同。

享元接口

public interface Flyweight {
    public double getHeight();   //返回内部数据
    public double getWidth();
    public double getLength();
    public void printMess(String mess);    //使用参数mess获取外部数据
}

享元工厂(FlyweightFactory)与具体享元
享元工厂是FlyweightFactory类,负责创建和管理享元对象。FlyweightFactory将创建享元对象的具体享元类ConcreteFlyweight类作为自己的内部类。

public class FlyweightFactory {
    private HashMap<String, Flyweight> hashMap;
    static FlyweightFactory factory = new FlyweightFactory();

    private FlyweightFactory() {
        hashMap = new HashMap<String, Flyweight>();
    }

    public static FlyweightFactory getFactory() {
        return factory;
    }

    public synchronized Flyweight getFlyweight(String key) {
        if (hashMap.containsKey(key))
            return hashMap.get(key);
        else {
            double width = 0, height = 0, length = 0;
            String[] str = key.split("#");
            width = Double.parseDouble(str[0]);
            height = Double.parseDouble(str[1]);
            length = Double.parseDouble(str[2]);
            Flyweight ft = new ConcreteFlyweight(width, height, length);
            hashMap.put(key, ft);
            return ft;
        }
    }

    class ConcreteFlyweight implements Flyweight {  //ConcreteFlyweight是内部类
        private double width;
        private double height;
        private double length;

        private ConcreteFlyweight(double width, double height, double length) {
            this.width = width;
            this.height = height;
            this.length = length;
        }

        public double getHeight() {
            return height;
        }

        public double getLength() {
            return length;
        }

        public double getWidth() {
            return width;
        }

        public void printMess(String mess) {
            System.out.print(mess);     //输出外部数据mess
            System.out.print(" 宽度:" + width);  //输出内部数据width
            System.out.print(" 高度:" + height);
            System.out.println(" 长度:" + length);
        }
    }
}

Application

public class Application {
    public static void main(String[] args) {
        FlyweightFactory factory = FlyweightFactory.getFactory();
        double width = 1.82,height = 1.47,length = 5.12;
        String key = ""+width+"#"+height+"#"+length;
        Flyweight flyweight = factory.getFlyweight(key);
        Car aodiA6One = new Car(flyweight,"奥迪A6","黑色",128);
        Car aodiA6Two = new Car(flyweight,"奥迪A6","灰色",160);
        //aodiA6One和aodiA6Two没有向享元传递外部数据,而是获取享元的内部数据
        aodiA6One.print();
        aodiA6Two.print();
        width = 1.77;
        height = 1.45;
        length = 4.63;
        key = ""+width+"#"+height+"#"+length;
        flyweight = factory.getFlyweight(key);
        Car aodiA4One = new Car(flyweight,"奥迪A4","蓝色",126);
        Car aodiA4Two = new Car(flyweight,"奥迪A4","红色",138);
        //aodiA4One和aodiA4Two向享元传递外部数据,这些数据是不共享的
        flyweight.printMess(" 名称:奥迪A4 颜色:蓝色 功率:126");
        flyweight.printMess(" 名称:奥迪A4 颜色:红色 功率:138");
    }
}
class Car {
    Flyweight flyweight;
    String name,color;
    int power;

    public Car(Flyweight flyweight,String name,String color,int power){
        this.flyweight = flyweight;
        this.name = name;
        this.color = color;
        this.power = power;
    }

    public void print(){
        System.out.print(" 名称:"+name);
        System.out.print(" 颜色:"+color);
        System.out.print(" 功率:"+power);
        System.out.print(" 宽度:"+flyweight.getWidth());
        System.out.print(" 高度:"+flyweight.getHeight());
        System.out.println(" 长度:"+flyweight.getLength());
    }
}

运行效果图
在这里插入图片描述

1.享元模式的优点

(1)使用享元可以节省内存的开销,特别适合处理大量细粒度对象,这些对象的许多属性值是相同的,而且一旦创建则不容许修改。

(2)享元模式中的享元可以使用方法的参数接受外部状态中的数据,但外部状态数据不会干扰到享元中的内部数据,这就使享元可以在不同的环境中被共享。

2.适合使用享元模式的情景

(1)一个应用程序使用大量的对象,这些对象之间部分属性本质上是相同的,这时应使用享元来封装相同的部分。

(2)对象的多数状态都可改变为外部状态,就可以考虑将这样对象作为系统中的享元来使用。


(七)桥接模式

桥接模式:将抽象部分与它的实现部分分离,使得他们都可以独立地变化。

桥接模式包括4种角色:

  • 抽象(Abstration):是一个抽象类,该抽象类含有Implementor声明的变量,即维护一个Implementor类型对象。

  • 实现者(Implementor):实现者角色是一个接口(抽象类),该接口(抽象类)中的方法不一定与Abstration类中方法一致。Implementor接口(抽象类)负责定义基本操作,而Abstration类负责定义基于这些基本操作的较高层次的操作。

  • 细化抽象(Refined Abstration):细化抽象是抽象角色的一个子类,该子类在重写(覆盖)抽象角色中的抽象方法时,在给出一些必要的操作后,将委托所维护Implementor类型对象调用相应的方法。

  • 具体实现者(Concrete Implementor):具体实现者是实现(扩展)Implementor接口(抽象类)的类。

桥接模式的类图:
桥接模式类图

例子:

计算建筑楼房的成本

抽象

public abstract class ArchitectureCose{
    BuildingDesign design;
    double unitPrice;
    public abstract double giveCost();
}

细化抽象

public class BuildingCose extends ArchitectureCose{

    BuildingCose(BuildingDesign design, double unitPrice){
        this.design = design;
        this.unitPrice = unitPrice;
    }

    public double giveCost(){
        double area = design.computerArea();
        return area*unitPrice;
    }
}

实现者

public interface BuildingDesign {
    public double computerArea();
}

具体实现者

public class HouseDesign implements BuildingDesign{
    double width, length;
    int floorNumber;
    HouseDesign(double width, double length, int floorNumber){
        this.width = width;
        this.length = length;
        this.floorNumber = floorNumber;
    }
    public double computerArea(){
        return width*length*floorNumber;
    }
}

Application

public class Application {
    public static void main(String[] args) {
        double width = 63, height = 30;
        int floorNumber = 8;
        double unitPrice = 6867.38;
        BuildingDesign design = new HouseDesign(width, height, floorNumber);
        System.out.println("宽:"+width+"米,高:"+height+"米,层数为:"+floorNumber);
        ArchitectureCose cost = new BuildingCose(design,unitPrice);
        double price = cost.giveCost();
        System.out.println("每平方米造价:"+unitPrice+"元,商业楼的建设成本:"+price);
    }
}

运行效果图
在这里插入图片描述

1.桥接模式的优点

(1)桥接模式分离实现与抽象,使抽象和实现可以独立的扩展。当修改实现的代码时,不影响抽象的代码,反之也一样。

(2)满足开-闭原则。抽象和实现者处在同层次,使系统可独立地扩展者两个层次。增加新的具体实现者,不需要修改细化对象,反之增加新的细化对象也不需要修改具体实现。

2.适合使用桥接模式的情景

(1)不想让抽象和某些重要的实现代码是固定绑定关系,这部分实现可运行时动态决定。

(2)抽象和实现者都可以继承的方法独立地扩充而互不影响,程序在运行期间可能需要动态的将一个抽象的子类的实例与一个实现者的子类的实例进行组合。

(3)希望对实现者层次的代码的修改对抽象层不产生影响,即抽象层的代码不必重新编译,反之亦然。


待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值