一、理解
(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)组合
如下例子:
一个公司的结构,
公司接口类:
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个函数提前组合起来,对外形成一个调用,客户自需调用这一个调用就能完成这个业务,
代码示列:
入上图,义务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("输出响应参数");
}
}