一、理解

(1)适配器:见上篇文章,适配器设计模式

(2)桥接:对产品进行多个维度的抽象,每个抽象相互独立实现,用聚集的方式将这些抽象聚集在某一个抽象中实现相互联系,每个抽象的扩展对其他抽象五影响。

(3)组合:将一组对象组合成树形结构,一棵子子树可用用其根对象来表示某些特征,根对象有孩子对象的引用。对外在使用叶子对象和使用非叶子对象没有任何差别。

(4)装饰:动态的给对象增加某些功能。装饰器实现或继承待装饰对象的接口或类,聚集待装饰对象(适配器里有需要待装饰对象引用)。

(5)外观:对用户而言是一个使用系统的前台,可用通过其实现对系统的所有功能的使用。

(6)享元:用共享技术实现对象的分时共享使用,和单例不同的是,它可用有多个对象,对象的创建只有在享元工厂里没有剩余需求对象时才创建,这不仅减少对象的个数,还尽量满足客户的需求。

(7)代理:代理对其他对象的访问,即可用通过代理访问到其他对象。

二、代码实现

(1)桥接

是避免类数量爆炸的有效方法,比如,电脑,由台式机、笔记本,但每种电脑还对应不同的生成厂商苹果、华为、联想、神州等,如我们用继承方式来组合,有,苹果台式机、苹果笔记本、华为台式机、华为笔记本、联想台式机、联想笔记本、神州台式机、神州笔记本,可以看到这就有8个子类。如果现在我们增加了一种电脑,手机,那么就必须增加每个厂商对应的手机产品类,这扩展起来太多太麻烦。
现在我们使用桥接模式,将电脑分为两个维度,厂商、类型,两类型之间建里一种联系(即桥接)。

示列代码如下:

品牌:

public interface brand {     //品牌接口
        public void getname();

}

public class huawei implements brand {  //华为品牌实现类
       String name="华为";
       public void getname() {
             System.out.println(name);
       }
}

public calss apple implements brand {    //苹果品牌实现类
        String name = "苹果";
        public void getname() {
             System.out.println(name);
        }
}

类型:
这个类对用户而言将生成一个有类型有产品的电脑

public abstract class computer {    //类型接口
        brand p;           //品牌聚聚在这里
        public void setbrand(brand p) { 
              this.p=p;
        }
        public void getname();
}

public calss desktop extends computer {     //台式类型实现类
        public desktop(brand p){
            super(p);
        }
        public void getname(){
            t.getname();
            System.out.println("台式");
        {
}

public class laptop extends computer {       //手提类型实现类
       public void laptop(brand p) {
              super(p);
       }
        public void getname() {
              System.out./println("手提");
        }
}

测试:

public class test {
     public static void main(String agrc) {
         brand p= new huawei();
         new computer t =desktop(p);
         t.getname();   //将得到 华为 台式
     }

}

(2)组合

如下例子:

v2-9b88bff2231f77bff5a8203b51352f6f_720w_LI.jpg

一个公司的结构,

公司接口类:

public interface Company {     //总接口,员工和部门都实现这个接口
    int getAge();
}

员工类,这里只放员工A1的代码实现,其他员工的代码和这个一样

public class EmployeeA1 implements Company {
    private int age;

    public EmployeeA1(int age) {
        this.age = age;
    }

    @Override
    public int getAge() {
        return this.age;
    }
}

部门类,这里只放部门A的代码实现,部门B和这个一样

public class DepartmentA implements Company {

    private List<Company> list = new ArrayList<>();  //存放该部门下的员工或部门
    private String name;

    public void add(Company company) {      //部门下增加员工或部门
        list.add(company);
    }

    @Override
    public int getAge() {      // 统计部门下所有员工的年龄总和
        int age = 0;
        for (Company company : list) {      //循环统计该部门下的员工和部门信息
            age = age + company.getAge();      //如果此时company为部门,这会递归统计
        }
        return age;
    }
}

在客户端调用之前要把公司的人员结构和年龄设置好

public static Company getCompany() {       //写一个静态方法来构造这个数型结构   返回的是根
    EmployeeA1 employeeA1 = new EmployeeA1(25); // 部门A下的1号员工
    EmployeeA2 employeeA2 = new EmployeeA2(26); // 部门A下的2号员工
    EmployeeB1 employeeB1= new EmployeeB1(30); // 部门B下的1号员工
    EmployeeB2 employeeB2= new EmployeeB2(30); // 部门B下的2号员工
    DepartmentB departmentB = new DepartmentB(); // 组装部门B
    departmentB.add(employeeB1);
    departmentB.add(employeeB2);
    DepartmentA departmentA = new DepartmentA(); // 组装部门A
    departmentA.add(employeeA1);
    departmentA.add(employeeA2);
    departmentA.add(departmentB);// 部门B也属于部门A
    return departmentA;
}

下面开始统计公司员工的年龄

Company company = getCompany(); // 获取公司的人员架构
System.out.println(company.getAge()); // 统计年龄

(3)装饰
装饰类实现待装饰对象的抽象接口(或父类),并获取待装饰对象引用,重写实现接口方法来实现

代码示列:

public class car {            //车
     public void show() {
           System.out.println("百公里油耗10L");
     }
}

public class colordecorator extends car {   //装饰器
     car car;
     public calordecorator(car ca) {
           this.car = ca;
     }
     public void show() {
           car.show();
           System.out.println("白色");
     }
}

public class test {
     public static void main(String[ agrc]) {
           car ca = new();
           colordecorator baicar = new colordecorator(ca);
           baicar.show();       //显示百公里油耗10L  白色
     }
}

(4)外观
就是将一个业务需要调用的接口组合起来,让用户不必和繁杂的接口打交道,比如完成一个业务要10个函数协调完成,外观模式就是将这10个函数提前组合起来,对外形成一个调用,客户自需调用这一个调用就能完成这个业务,

代码示列:

屏幕截图 05-06 221708.png

入上图,义务A要接口1、2、3来完成,业务B要4、5接口来完成,我们增减两个接口表示业务,接口A和接口B

首先是中台系统的5个接口,这里用5个方法代表接口

public class Center {
    public void method1(){} // 模拟接口1
    public void method2(){} // 模拟接口2
    public void method3(){} // 模拟接口3
    public void method4(){} // 模拟接口4
    public void method5(){} // 模拟接口5
}

接下来是中台系统中的外观类,里面包含了接口A和接口B。同样用两个方法来代表接口

在接口A中调用了中台系统的前3个方法;在接口B中调用了中台系统中的后两个方法

public class CenterFacade {
    private Center center = new Center();   //也可用用继承的方法

    // 模拟接口A
    public void centerA() {
        center.method1();
        center.method2();
        center.method3();
    }

    // 模拟接口B
    public void centerB() {
        center.method4();
        center.method5();
    }
}

业务系统A在调用时,使用外观类中的接口A即可实现自己的业务逻辑

CenterFacade centerFacade = new CenterFacade();
centerFacade.centerA();

业务系统B在调用时,使用外观类中的接口B即可实现自己的业务逻辑

CenterFacade centerFacade = new CenterFacade();
centerFacade.centerB();

(5)享元

1、享元工厂(Llibrary):用于创建具体享元类,维护相同的享元对象。当请求对象已经存在时,直接返回对象,不存在时,在创建对象。在例子中的解释就是图书馆,保存了所有的书,当学生借书时,有就拿走,没有买一本新书。这里面其实是使用了单例模式的。

2、抽象享元(Book):定义需要共享的对象业务接口。享元类被创建出来总是为了实现某些特定的业务逻辑.

3、具体享元(ConcreteBook):实现抽象享元类的接口,完成某一具体逻辑。在这里表示可以被借出。

在这里享元工厂是享元模式的核心,它需要确保系统可以共享相同的对象。它会维护一个对象列表,当我们想要获取享元类时,如果请求的享元类已经被创建,则直接返回已有的享元类:若没有,则创建一个新的享元对象,并将它加入到维护队列中。

代码实现

第一步:定义抽象享元类(Book)

public interface Book {
    public void borrow();
}

第二步:定义具体享元类(ConcreteBook)

public class ConcreteBook implements Book {

//被借出的书名
private String name;
public ConcreteBook(String name) {
    this.name = name;
}
@Override
public void borrow() {
    System.out.println("图书馆借出一本书,书名为:" + this.name );
}

}

第三步:享元工厂(Llibrary)

public class Library {

//图书馆维护一个图书列表
private Map<String, Book> bookPools = new HashMap<String, Book>();
private static Library factory = new Library();
//图书馆只有一个
public static Library getInstance(){
    return factory;
}
//图书馆外借图书
public Book libToBorrow(String bookName){
    Book order = null;
    //如果书架有,直接借出
    if (bookPools.containsKey(bookName)) {
        order = bookPools.get(bookName);
    }
    //如果书架没有,那就调进来一本新书
    else{
        order = new ConcreteBook(bookName);
        bookPools.put(bookName, order);
    }
    return order;
}
//图书馆书架上的书的数量
public int getAllBookSize(){
    return bookPools.size();
}

}

第四步:模拟学生去借书

public class Student {

//图书馆书架上的书
private static List<Book> books = new ArrayList<Book>();
private static Library library;
public static void main(String[] args) {
    library = Library.getInstance();
    studentBorrow("java编程思想");
    studentBorrow("java核心卷一");
    studentBorrow("java从入门到精通");
    System.out.println("后两本没学会,又借了一次 ");
    studentBorrow("java核心卷一");
    studentBorrow("java从入门到精通");
    //把每一本书借出去
    for (Book book : books) {
        book.borrow();
    }
    //输出一些学生一共借多少本书
    System.out.println("学生一共借了 " + books.size() + " 本书! ");
    //输出一下图书馆一共借出多少本书
    System.out.println("图书馆实际借出" + library.getAllBookSize() + " 本书");
}
private static void studentBorrow(String bookName) {
    books.add(library.libToBorrow(bookName));
}

}

在上面其实学生一共借了5次书,但是有两本是重复的,所以对于图书馆来说,其实是借出去了三本书。最后我们看一下输出结果吧:

//图书馆借出去一本书,书名为:java编程思想
//图书馆借出去一本书,书名为:java核心卷一
//图书馆借出去一本书,书名为:java从入门到精通
//后两本没学会,又借了一次 
//图书馆借出去一本书,书名为:java核心卷一
//图书馆借出去一本书,书名为:java从入门到精通

//学生一共借了 5 本书! 
//图书馆实际借出了 3 本书

(7)代理
实现待代理对对象的接口,保存待代理对象的引用。

假如我们程序里具备了支付功能,有一个支付接口 Payment

public interface Payment {
    void doPay();
}

并且有一个支付宝实现类 AliPayment

 public class AliPayment implements Payment{
        @Override
        public void doPay() {
            System.out.println("支付宝支付");
        }
    }

接下来我们要在支付宝支付之前输出请求参数和响应参数

public class LogPaymentProxy implements Payment {  //实现接口

        private Payment payment;         //有代理的对象引用
    
        public LogPaymentProxy(Payment payment) {
            this.payment = payment;
        }
    
        @Override
        public void doPay() {
            System.out.println("输出请求参数");
            payment.doPay();
            System.out.println("输出响应参数");
        }
    }