设计模式一:适配器模式
现在我们有两个类Dog和Cat,这两个类都有发出声音的功能
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Cat { public void catCall() { System.out.println("喵!喵!喵!"); } } public class Dog { public void dogCall() { System.out.println("汪!汪!汪!"); } }
然后增加需求,获取动物的叫声,这时就要把这两个类的发声功能整合到一个接口:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public interface Animal { void call(); }
这个很简单,直接重新设计代码,使Cat和Dog实现接口就行。但是考虑到Dog和Cat两个类来自于第三方Jar包,没有办法进行重构,就使用设配器模式,使Cat和Dog类分别统一到接口的调用!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/**Cat的设配器类 对象适配器模式!*/ public class CatAdapter implements Animal { Cat cat = new Cat(); public void call() { cat.catCall(); } } /** Dog类的设配器类 类适配器模式! */ public class DogAdapter extends Dog implements Animal { public void call() { this.dogCall(); } }
这时Cat和Dog的调用:测试
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 测试*/ public class App { public static void main(String[] args) { Animal cat = new CatAdapter(); Animal dog = new DogAdapter(); cat.call(); dog.call(); } }
到这里就完成了Cat和Dog的接口统一了。不过在对Cat和Dog的适配中选用的适配方式不同。CatAdapter是使用一个Cat实例实现接口函数,而DogAdaptec是继承Dog类使用父类函数实现接口函数,这两种模式分别叫做 对象的适配器模式和类的适配器模式。
设计模式二:外观模式
现在我有2个子模块,分别是加、减
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 加法 子模块*/ public class Plus { public int plus(int a, int b) { return a + b; } } /** 减法 子模块*/ public class Minus { public int minus(int a, int b) { return a - b; } }
那么现在客户端A想要使用上面的两个子模块实现一个 1+2-3+4的功能
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 客户端 A*/ public class ClientA { public int handle() { Minus minus = new Minus(); Plus plus = new Plus(); return plus.plus(minus.minus(plus.plus(1, 2), 3), 4); } }
如果这个时候有客户端B也要实现这样的功能,甚至是1+2-3+4-5+6的功能,这时在想ClientA一样实现的话,会显得代码重复臃肿,而且当子模块间的逻辑发生变化时,会牵连到客户端A和B的模块代码改变。这时,增加一个外观类来定义一些常用的子模块操作就非常有必要了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 外观类*/ public class Facade { public int handle(int begin, int end) { Minus minus = new Minus(); Plus plus = new Plus(); int res = begin++; while (begin <= end) { res = plus.plus(res, begin++); if (begin > end) break; res = minus.minus(res, begin++); } return res; } }
这时ClientA的代码变为
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 客户端 A*/ public class ClientA { public int handle() { /*Minus minus = new Minus(); Plus plus = new Plus(); return plus.plus(minus.minus(plus.plus(1, 2), 3), 4);*/ Facade facade = new Facade(); return facade.handle(1, 4); } }
此时ClientA,B的重复代码减少,而且不用关注子系统的调用逻辑,只需要请求Façade类提供的调用就行,降低程序的耦合性。而当子系统有增删和逻辑变动时,需要同时更新外观类的逻辑即可。
设计模式三:组合模式
组合模式的定义:将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
以员工的层级关系为例,现在有empA,和empB两个等级的员工,如果老板要让所有员工work,他将调用所有对象的work函数,这样老板的工作量太大,而且现实中也不会这样处理。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** B类员工 */ public class EmpB { private String name; public EmpB(String name) { this.name = name; } public void work() { System.out.println(name + " begin work!"); } } /** A员工类*/ public class EmpA { private String name; public EmpA(String name) { this.name = name; } public void work() { System.out.println(name + " begin work!"); } } public class App { public static void main(String[] args) { EmpA a001 = new EmpA("001"); EmpB b002 = new EmpB("002"); EmpB b003 = new EmpB("003"); //工作 a001.work(); b002.work(); b003.work(); } }
修改代码,让EmpA去指挥EmpB工作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/**A员工类,管理EmpB员工*/ public class EmpA { private String name; List<EmpB> empBs = new ArrayList<EmpB>(); public EmpA(String name) { this.name = name; } public void work() { System.out.println(name + " begin work!"); } public void add(EmpB empB) { empBs.add(empB); } } public static void main(String[] args) { EmpA a001 = new EmpA("001"); EmpB b002 = new EmpB("002"); EmpB b003 = new EmpB("003"); a001.add(b002); a001.add(b003); //工作 a001.work(); b002.work(); }
现在老板可以直接指派001工作,不用管001这么让EmpB员工工作,也可以直接指挥002工作。
这就是简单的组合模式,在这样的情况下,老板对单个员工和一个小组的调用是一样的。
更多提升,因为,员工暴露的接口都是一致的,所以可以使用接口或者抽象类进行函数统一,比如work()或者add()。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 抽象类*/ public abstract class Emp { protected String name; protected List<Emp> emps = new ArrayList<Emp>(); protected Emp(String name) { this.name = name; } public abstract void work(); public abstract void add(Emp emp); }
设计模式四:桥接模式
公司要生产一个产品,可以向用户提供展示数据和接受修改的功能,但是这个项目人手不足,底层的数据的读写存储功能无法按时完成。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 具有展示数据和修改数据功能的产品 没有读写数据功能*/ public class Product { public String display() { //获取存错数据 String msg = null; return msg; } public void update(String msg) { //先存储写数据 } }
这时就想把这个读写存储功能外包给多个第三方开发,在发布任务的时候就先把自己需要的规范提出来,也就是自己要的接口,然后要求所有第三方都按照这个接口实现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 接口*/ public interface DataBase { void write(String msg); String read(); } /** 具有展示数据和修改数据功能的产品*/ public class Product { private DataBase dataBase; public Product(DataBase dataBase) { this.dataBase = dataBase; } public String display() { return dataBase.read(); } public void update(String msg) { //先存储写数据 dataBase.write(msg); } }
这时候第三方就根据接口进行实现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** A对接口的实现*/ public class DataBaseA implements DataBase { private String msg; public void write(String msg) { this.msg = msg; } public String read() { return this.msg; } } /**B对接口的实现*/ public class DataBaseB implements DataBase { private static String msg; public void write(String msg) { DataBaseB.msg = msg; } public String read() { return msg; } }
这时公司就可以根据自己的需求来使用这两个第三方功能了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 测试*/ public class App { public static void main(String[] args) { Product productA = new Product(new DataBaseA()); Product productB = new Product(new DataBaseB()); productA.update("use a"); System.out.println(productA.display()); productB.update("use b"); System.out.println(productB.display()); } }
这就是桥接模式。最常见的就是数据库的接口了!
桥接模式和适配器模式一样,都是为上层调用统一api接口,不过适配器是多个底层接口存在,自下而上的设计;桥接模式的接口设计时,下层接口是抽象不存在的,是自上而下的设计。
设计模式五:责任链模式
现在有一个日志输出接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 日志输出接口*/ public interface LogPrinter { void log(String msg); }
分别实现三个级别的输出,Info,Warn,Error输出Warn消息的时候要输出Info消息,输出Error消息时同时输出Warn和Info消息。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 普通消息输出*/ public class InfoLogPrinter implements LogPrinter { public void log(String msg) { System.out.println("info: " + msg); } } /**警告输出*/ public class WarnLogPrinter { public void log(String msg) { System.out.println("info: " + msg); } } /** 错误消息输出*/ public class ErrorLogPrinter implements LogPrinter { public void log(String msg) { System.out.println("info: " + msg); } }
构建日志打印类,提供三种级别方法的调用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 日志记录器*/ public class Logger { private LogPrinter info = new InfoLogPrinter(); private WarnLogPrinter warn = new WarnLogPrinter(); private ErrorLogPrinter error = new ErrorLogPrinter(); public void info(String msg) { info.log(msg); } public void warn(String msg) { warn.log(msg); info(msg); } public void error(String msg) { error.log(msg); warn(msg); } }
从上面的代码可以看出,Logger包含了三个日志实例,每个实例都有可能去处理日志消息,如果处理日志的类不断增多,那么Logger会与更多的LogPrint耦合在一起。为了解决这个问题,我们使用责任链模式进行处理。
在上面的代码进行修改:增加一个抽象类实现输出接口,并且维护责任链设计
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 日志输出抽象类*/ public abstract class AbstractLogPrinter implements LogPrinter { protected static int INFO = 1; protected static int WARN = 2; protected static int ERROR = 3; private AbstractLogPrinter nextLogPrinter; protected int level; public void setNextLogPrinter(AbstractLogPrinter nextLogPrinter) { this.nextLogPrinter = nextLogPrinter; } public void printlog(int level, String msg) { if (this.level <= level) { log(msg); } if (nextLogPrinter != null) { nextLogPrinter.printlog(level, msg); } } }
三种状态的对应输出类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/**普通消息输出*/ public class InfoLogPrinter extends AbstractLogPrinter { public InfoLogPrinter() { this.level = AbstractLogPrinter.INFO; } public void log(String msg) { System.out.println("info: " + msg); } } /** 警告输出 */ public class WarnLogPrinter extends AbstractLogPrinter { public WarnLogPrinter() { this.level = AbstractLogPrinter.WARN; } public void log(String msg) { System.out.println("info: " + msg); } } /**错误消息输出*/ public class ErrorLogPrinter extends AbstractLogPrinter { public ErrorLogPrinter() { this.level = AbstractLogPrinter.ERROR; } public void log(String msg) { System.out.println("info: " + msg); } }
日志记录API:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 日志记录器*/ public class Logger { private AbstractLogPrinter logprint = null; public Logger() { AbstractLogPrinter info = new InfoLogPrinter(); AbstractLogPrinter warn = new WarnLogPrinter(); AbstractLogPrinter error = new ErrorLogPrinter(); info.setNextLogPrinter(warn); warn.setNextLogPrinter(error); logprint = info; } public void info(String msg) { logprint.printlog(AbstractLogPrinter.INFO, msg); } public void warn(String msg) { logprint.printlog(AbstractLogPrinter.WARN, msg); } public void error(String msg) { logprint.printlog(AbstractLogPrinter.ERROR, msg); warn(msg); } }
在API实现中,只需要拥有一个日志记录器,就可以进行记录。第一个日志记录器将消息传递给下一个消息记录器。
在Web开发中的过滤器和拦截器就采用了此类设计模式,在开发时我们只需要编写对应的逻辑类,放入处理链中即可。
设计模式六:单体模式
单体模式指在整个JVM运行过程在中这个Class只有一个实例。
第一步:这个Class只有一个实例,那么我们需要先私有化它的构造函数,保证外部无法实例化Class
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 单体类*/ public final class Singleton { private Singleton() { } }
如果有子类的话可以使用protected,防止客户端程序调用即可。
第二步:使用静态域维护实例,使用静态函数监控Class的实例创建
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//初始化时创建(饥饿模式) private static Singleton singleton = new Singleton(); private static Singleton getInstance() { return singleton; } //运行时创建(懒汉模式) private volatile static Singleton singleton = null; public static Singleton getInstance() { if (singleton == null) { singleton = new Singleton(); return singleton; } else { return singleton; } }
第三步,实例化时初始化属性,使用方法操作属性;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
private String name; private Singleton() { this.name = "singleton"; } public void updateName(String name) { } public String getName() { return name; }
这样一个单体类初步完成了,加上对多线程的考虑:
将属性的操作方法同步:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public synchronized void updateName(String name) {} public synchronized String getName() { return name; }
对应在初始化时创建函数,没有必要进行getInstance的同步。对于运行时创建,为了防止在对象为空时的多个线程访问导致多次创建,则需要在创建时同步:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; }
单体类和全是静态函数的类比较:1.单体类可以控制自己的初始化时机;2.(针对单例性而言)单体类可以继承和被继承而静态类不行。3.单体类可以根据需求变更为多体,具有灵活性。
单体还有一种模式叫多例模式,分为有上限的无上限的,是单例模式的自然推广,特点是自己管理创建过程和维护创建出的实例。
设计模式七:观察者模式
考虑到一种常见A获取数据,B对数据进行打印输出。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 简单实现*/ public class Simple { static class ClassA { private ClassB classB = new ClassB(); public void getData(String data) { classB.print(data); } } static class ClassB { public void print(String data) { System.out.println(data); } } public static void main(String[] args) { ClassA classA = new ClassA(); classA.getData("xxxx"); } }
在上面的ClassA和ClassB中修改代码可以实现我们现在的需求,但是如果我们不断的扩展,这时C要监听A的数据,D,E ……,这会导致我们不断的修改代码。这时候就可以使用观察者模式。
B,C,D都是消息需求者,A是被观察者,这时我们可以定义一个观察者 具有添加,移除,通知消息监听者的接口。统一监听者的接口,被观察者的数据接口
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 观察者的接口*/ public interface Observer { void addListener(Listener listener); void removeListener(Listener listener); void notifyListener(String data); } /** 监听者接口*/ public interface Listener { void notified(String msg); } /**被观察者接口*/ public interface Subject { void update(String string); } /** 使用一个类实现观察者和被观察者接口*/ public class ObserverImp implements Observer, Subject { Set<Listener> listeners = new HashSet<>(); @Override public void addListener(Listener listener) { listeners.add(listener); } @Override public void removeListener(Listener listener) { listeners.remove(listener); } @Override public void notifyListener(String data) { for (Listener listener : listeners) { listener.notified(data); } } @Override public void update(String string) { notifyListener(string); } }
测试
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 测试 */ public class App { public static void main(String[] args) { ObserverImp sub = new ObserverImp(); Listener lisA = (String data) -> { System.out.println("A :" + data); }; Listener lisB = (String data) -> { System.out.println("B :" + data); }; Listener lisC = (String data) -> { System.out.println("C :" + data); }; sub.addListener(lisA); sub.addListener(lisB); sub.addListener(lisC); sub.update("test1"); sub.removeListener(lisB); sub.update("test2"); } }
使用观察者模式后,可以使得在不修改原有基础代码,直接根据业务进行编码扩展。
设计模式八:中介模式
现在在办公室里,有ABCDE五个员工手里各有一份数据,工作时,A需要BD的数据,C需要AE的数据,C需要…… 如果这五个员工都要到其他员工的工位上去那数据,多乱啊。放在程序中也是,几个对象直接都相互持有,耦合性太强了。在办公室里,老板可以给公式配一个助理解决问题,那我我们可以增加一个中介啊,所有的类都和中介打交道,不去持有其他对象。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 同事抽象类*/ public abstract class Colleague { private int number; private Mediator mediator; public Colleague(int data) { number = data; } public int getNumber() { return number; } public Mediator getMediator() { return mediator; } public void setMediator(Mediator mediator) { this.mediator = mediator; } public abstract int getData(); } /** 中介接口*/ public interface Mediator { int getBC(); int getCA(); int getAB(); } /** 中介实现类*/ public class MediatorImp implements Mediator { private Colleague A, B, C; public MediatorImp(Colleague A, Colleague B, Colleague C) { this.A = A; this.B = B; this.C = C; } @Override public int getBC() { return B.getNumber() + C.getNumber(); } @Override public int getCA() { return A.getNumber() + C.getNumber(); } @Override public int getAB() { return B.getNumber() + A.getNumber(); } }
测试
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** 测试*/ public class App { public static void main(String[] args) { Colleague a = new Colleague(1) { @Override public int getData() { return getMediator().getBC(); } }; Colleague b = new Colleague(2) { @Override public int getData() { return getMediator().getCA(); } }; Colleague c = new Colleague(3) { @Override public int getData() { return getMediator().getAB(); } }; Mediator mediator = new MediatorImp(a, b, c); a.setMediator(mediator); b.setMediator(mediator); c.setMediator(mediator); System.out.println(a.getData()); System.out.println(b.getData()); System.out.println(c.getData()); } }
使用中介模式就将几个同事类之间进行了解耦合,逻辑都交由中介类去处理。