小结:开发时不要为了炫技而使用设计模式,适时参考即可
创建型
单例模式(singleton)
1.饿汉
例子:
public class Singleton {
// 静态字段引用唯一实例:
private static final Singleton INSTANCE = new Singleton();
// 通过静态方法返回实例:
public static Singleton getInstance() {
return INSTANCE;
}
// private构造方法保证外部无法实例化:
private Singleton() {
}
}
总结:
特点:上来就随着类的加载而创建实例
缺点:非懒加载
2.懒汉式及改进
例子:
package com.attoutouer.designPattern.singleton;
public class Lazy2 {
private static volatile Lazy2 singleton;
private Lazy2() {
System.out.println("俺懒汉实例化了");
}
public static Lazy2 getInstace(){
if(singleton == null){
synchronized (Lazy.class){
if(singleton == null){
singleton = new Lazy2(); //error
}
}
}
return singleton;
}
}
小结
1.(双重检查锁的由来):
为保证第一次创建对象时线程安全 在方法上加 synchronized,但会导致在第一次即使创建完对象了,不需要线程安全锁,线程每次也要等待,不能直接return拿到对象,效率太低。
那为了提高效率 尝试锁范围缩小到判断里
但又会使得第一次创建对象时 线程不安全,
那就锁里面再加一次判断,解决
2.用volatile修饰 是为了保证指令重排,理由如下:
上述1的写法看似解决了问题,但是仍有个很大的隐患:实例化对象的那行代码(标记为error的那行),实际上可以分解成以下三个步骤:
1分配内存空间
2初始化对象
3将对象指向刚分配的内存空间
但是有些编译器为了性能的原因,可能会将第二步和第三步进行重排序,顺序就成了:
1分配内存空间
2将对象指向刚分配的内存空间
3初始化对象
现在考虑重排序后,两个线程发生了以下调用:
Time | Thread A | Thread B |
---|---|---|
T1 | 检查到uniqueSingleton为空 | |
T2 | 获取锁 | |
T3 | 再次检查到uniqueSingleton为空 | |
T4 | 为uniqueSingleton分配内存空间 | |
T5 | 将uniqueSingleton指向内存空间 | |
T6 | 检查到uniqueSingleton不为空 | |
T7 | 访问uniqueSingleton(此时对象还未完成初始化) | |
T8 | 初始化uniqueSingleton |
在这种情况下,T7时刻线程B对uniqueSingleton的访问,访问的是一个初始化未完成的对象,所以要用volatile。
3.枚举
例子:
public enum World {
// 唯一枚举: INSTANCE;
private String name = "world";
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
总结: 有效防止反射攻击。
工厂模式 (factory)
总结:
将if else 的判断生成交给子类完成
1.简单工厂: 一个具体工厂,if else
2.工厂方法和抽象工厂的区别: 后者有 多个抽象产品角色 每个具体工厂可以生成多类型产品
参考链接: 设计模式:工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)-阿里云开发者社区
原型模式 (prototype)
例子:
public class PizzaStore1 implements Cloneable{
ApplePizza applePizza ;
public static Pizza orderPizza(String name){
Pizza pizza = null;
if("ApplePizza".equals(name)){
pizza = new ApplePizza(name);
}else if("BananaPizza".equals(name)){
pizza = new BananaPizza(name);
}
return pizza;
}
public ApplePizza getApplePizza() {
return applePizza;
}
public void setApplePizza(ApplePizza applePizza) {
this.applePizza = applePizza;
}
@Override
protected PizzaStore1 clone() throws CloneNotSupportedException {
PizzaStore1 clone = (PizzaStore1) super.clone();
clone.setApplePizza(clone.getApplePizza().clone());
return clone;
}
}
总结:
实现Cloneable接口,实现深拷贝;
建造者模式 (builder)
总结:
当一个类的表示(属性)基本不变,但构建过程比较复杂时,可以将此过程抽象出来。
参考链接:创建型-建造者模式-阿里云开发者社区
结构型
适配器模式 (adapter)
总结:
将被适配的类按照某种规范进行改造。进阶:一般直接组合该类到adapter,当需要被改造的类比较多时,也可以在构造器处声明一个它们共属的接口。
例子:
public class AC220 {
public int outputAC220V(){
int output = 220;
System.out.println("输出交流电"+output+"V");
return output;
}
}
public interface DC5 {
int outputDC5V();
}
public class PowerAdapter implements DC5{
AC220 ac220 = new AC220();
@Override
public int outputDC5V() {
int i = ac220.outputAC220V();
System.out.println("使用PowerAdapter输入AC"+i+"V"+"输出DC"+i/44+"V");
return i/44;
}
}
桥接模式(bridge)
总结:
1.将两种具有多个维度的类,进行组合,当然一般直接组合接口道另一个抽象类,不用第三者。避免类爆炸
2.桥接模式实现比较复杂,实际应用也非常少,但它提供的设计思想值得借鉴,即不要过度使用继承,而是优先拆分某些部件,使用组合的方式来扩展功能。
例子:
public abstract class Iphone {
Color color;
public Iphone(Color color) {
this.color = color;
}
public abstract void draw();
}
public interface Color {
public void bePaint(String shape);
}
public class Blue implements Color {
@Override
public void bePaint(String shape) {
System.out.println("蓝色的"+shape);
}
}
public class XIAOMI extends Iphone{
public XIAOMI(Color color) {
super(color);
}
@Override
public void draw() {
color.bePaint("小米");
}
}
public class Test {
public static void main(String[] args) {
Color blue = new Blue();
Iphone xiaomi = new XIAOMI(blue);
xiaomi.draw();
}
}
装饰器模式(decorator)
总结:
1.
Decorator模式有什么好处?它实际上把核心功能和附加功能给分开了。核心功能指FileInputStream这些真正读数据的源头,附加功能指加缓冲、压缩、解密这些功能。如果我们要新增核心功能,就增加Component的子类,例如ByteInputStream。如果我们要增加附加功能,就增加Decorator的子类,例如CipherInputStream。两部分都可以独立地扩展,而具体如何附加功能,由调用方自由组合,从而极大地增强了灵活性
2.实现上:
//创建原始的数据源:
InputStream fis = new FileInputStream("test.gz");
// 增加缓冲功能:
InputStream bis = new BufferedInputStream(fis);
// 增加解压缩功能:
InputStream gis = new GZIPInputStream(bis);
即 抽象的装饰器类的 构造器定义 核心组件的公属接口即可
参考例子:装饰器 - 廖雪峰的官方网站
组合模式(composite)
总结:
用于树形结构的类,特点: 有子节点的list,和对应的add,remove 方法。像文件夹和文件、GUI窗口的各种组件,都符合Composite模式的定义,因为它们的结构天生就是层级结构。
参考例子:
组合 - 廖雪峰的官方网站
外观模式(facade)
总结:
将子系统的各个类整合起来,对外提供一个统一的入口,例如controller,网关。
例子:开公司:
// 工商注册:
public class AdminOfIndustry {
public Company register(String name) {
...
}
}
// 银行开户:
public class Bank {
public String openAccount(String companyId) {
...
}
}
// 纳税登记:
public class Taxation {
public String applyTaxCode(String companyId) {
...
}
}
public class Facade {
public Company openCompany(String name) {
Company c = this.admin.register(name);
String bankAccount = this.bank.openAccount(c.getId());
c.setBankAccount(bankAccount);
String taxCode = this.taxation.applyTaxCode(c.getId());
c.setTaxCode(taxCode);
return c;
}
}
享元模式(flyweight)
总结:
各种池化技术。连接池,线程池,Integer.valueof呀
参考例子:享元 - 廖雪峰的官方网站
代理模式(proxy)
总结:
与adapter模式类似都是转化接口,不过Adapter模式,它用于把A接口转换为B接口,而Proxy模式不是把A接口转换成B接口,它还是转换成A接口。
常见三种代理:
静态代理:实现目标类的接口
public class UserDaoProxy implements IUserDao{
private IUserDao target;
public UserDaoProxy(IUserDao target) {
this.target = target;
}
@Override
public void save() {
System.out.println("开启事务");//扩展了额外功能
target.save();
System.out.println("提交事务");
}
}
jdk动态代理: 通过反射,需要目标类必须实现接口
cglib代理:用于没实现接口的目标类,它是一个第三方代码生成包。
参考例子:https://segmentfault.com/a/1190000011291179
行为型
模板方法模式(Template Method)
总结:
抽象父类的方法 把主要骨架步骤完成,有变化的方法 作为抽像方法 交由子类实现。
参考例子:模板方法 - 廖雪峰的官方网站
命令模式(Command)
总结:
一般的系统来讲,调用者与被调用者是紧耦合的,但当系统复杂到对命令本身进行管理(执行、撤销、记录等等),就可以考虑用命令模式。直观例子:遥控器就是人和家用电器之间的一个命令管理者。
参考例子:命令 - 廖雪峰的官方网站
访问者模式(Visitor)
总结:
有一段数据结构和操作数据结构的方法混合代码,当我们希望增加类似操作方法时,为了避免修改源代码 就可以考虑此模式
例子:
public class FileStructure {
private File rootDir;
public FileStructure(File rootDir) {
this.rootDir = rootDir;
}
public void handle(Visitor visitor){
scan(rootDir,visitor);
}
private void scan(File rootDir, Visitor visitor) {
for (File file : rootDir.listFiles()) {
if (file.isFile()) {
visitor.visitFile(file);
} else if (file.isDirectory()) {
visitor.visitFile(file);
scan(file, visitor);
}
}
}
}
public interface Visitor {
public void visitFile(File file);
public void visitDir(File dir);
}
public class JavaFileVisitor implements Visitor {
@Override
public void visitFile(File file) {
if(file.getName().endsWith(".java")){
System.out.println("Found java file: " + file);
}
}
@Override
public void visitDir(File dir) {
System.out.println("Visit dir: " + dir.getName());
}
}
public class MainTest {
public static void main(String[] args) {
FileStructure fileStructure = new FileStructure(new File("E:\\89sm\\code"));
fileStructure.handle(new JavaFileVisitor());
}
}
迭代器模式(Iterator)
总结:
让集合类都用统一的遍历方式,而不用关心其内部结构。记住:关键是返回一个Iterator对象,可以考虑内部类
例子:
public class ReverseArrayCollection<T> implements Iterable<T> {
private T[] array;
public ReverseArrayCollection(T... objs) {//改成这样是为了 不对外暴露集合类型
this.array = Arrays.copyOfRange(objs,0,objs.length);
}
@Override
public Iterator<T> iterator() {
return new ReverseIterator();
}
class ReverseIterator implements Iterator<T>{
int index;
public ReverseIterator() {
this.index = ReverseArrayCollection.this.array.length;
}
@Override
public boolean hasNext() {//继续遍历的条件
return index>0;
}
@Override
public T next() {//将元素移动到下一位 并返回元素
index--;
return array[index];
}
}
}
public class MainTest {
public static void main(String[] args) {
ReverseArrayCollection<Integer> collection = new ReverseArrayCollection<>(1,2,3);
for ( Iterator<Integer> iterator = collection.iterator();iterator.hasNext();){
System.out.println("iterator.next() = " + iterator.next());
}
}
}
观察者模式(Observer)/ 发布订阅模式
总结:
抽象出一个Observer(消息接收方)接口,便于扩展消息接收方的类型。发布方在其内部维护
private List<ProductObserver> observers = new ArrayList<>();
// 注册观察者:
public void addObserver(ProductObserver observer) {
this.observers.add(observer);
}
// 取消注册:
public void removeObserver(ProductObserver observer) {
this.observers.remove(observer);
}
即可。 实现 发布者和订阅者的解耦。
参考例子:观察者 - 廖雪峰的官方网站
中介者模式(Mediator)
总结:
一.当发现多个类之间交互复杂 可以考虑中间者来引入它们 并模拟交互逻辑。
二.这样每个类不需要知道它需要交互的类是哪些,也不需要写交互逻辑,都继承一个mediator,原来大篇幅交互逻辑 都只用一句话代替即可 this.mediator.sync(selfclassname,data); 例如 redis mysql es 之间的数据同步
参考例子:https://juejin.cn/post/6844903699643383821
备忘录模式(Momento)
总结:
三个角色
发起者originator:对state进行操作,提供一个保存memento的方法
备忘录momento:只对state操作
管理者caretaker:只对 momento进行操作
关键是 保存状态进备忘录的管理者时:发起者要提供一个有状态的备忘录对象给管理者 即:cr.setMemento(or.createMemento()); //保存状态
例子:
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Momento setMomento(){
return new Momento(state);
}
}
public class Momento {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public Momento(String state) {
this.state = state;
}
}
public class Caretaker {
private Momento momento;
public Momento getMomento() {
return momento;
}
public void setMomento(Momento momento) {
this.momento = momento;
}
}
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("1");
System.out.println("初始状态为"+originator.getState());
System.out.println("保存状态");
caretaker.setMomento(originator.setMomento());
originator.setState("2");
System.out.println("设置状态为"+originator.getState());
System.out.println("恢复状态为"+caretaker.getMomento().getState());
}
}
解释器模式(Interpreter)
总结:
定义语法树 对语句进行解析,如sql 和通配符 (了解即可);
状态模式(State)
总结:
当一个类的要干啥事是跟 状态切换有关的,就可以考虑用这个模式
记住 两种核心角色: 一堆状态类 ,和管理这些实现切换状态的类
例子:
public class ThreadContext {
private ThreadState state;
public ThreadContext() {
state = new New();
}
public ThreadState getState() {
return state;
}
public void setState(ThreadState state) {
this.state = state;
}
public void start() {
((New) state).start(this);
}
public void getCPU() {
((Runnable) state).getCPU(this);
}
public void suspend() {
((Running) state).suspend(this);
}
public void stop() {
((Running) state).stop(this);
}
public void resume() {
((Blocked) state).resume(this);
}
}
public class New extends ThreadState {
public New() {
stateName = "新建状态";
System.out.println("当前线程处于:新建状态.");
}
public void start(ThreadContext hj) {
System.out.print("调用start()方法-->");
if (stateName.equals("新建状态")) {
hj.setState(new Runnable());
} else {
System.out.println("当前线程不是新建状态,不能调用start()方法.");
}
}
}
//具体状态类:就绪状态
class Runnable extends ThreadState {
public Runnable() {
stateName = "就绪状态";
System.out.println("当前线程处于:就绪状态.");
}
public void getCPU(ThreadContext hj) {
System.out.print("获得CPU时间-->");
if (stateName.equals("就绪状态")) {
hj.setState(new Running());
} else {
System.out.println("当前线程不是就绪状态,不能获取CPU.");
}
}
}
//具体状态类:运行状态
class Running extends ThreadState {
public Running() {
stateName = "运行状态";
System.out.println("当前线程处于:运行状态.");
}
public void suspend(ThreadContext hj) {
System.out.print("调用suspend()方法-->");
if (stateName.equals("运行状态")) {
hj.setState(new Blocked());
} else {
System.out.println("当前线程不是运行状态,不能调用suspend()方法.");
}
}
public void stop(ThreadContext hj) {
System.out.print("调用stop()方法-->");
if (stateName.equals("运行状态")) {
hj.setState(new Dead());
} else {
System.out.println("当前线程不是运行状态,不能调用stop()方法.");
}
}
}
//具体状态类:阻塞状态
class Blocked extends ThreadState {
public Blocked() {
stateName = "阻塞状态";
System.out.println("当前线程处于:阻塞状态.");
}
public void resume(ThreadContext hj) {
System.out.print("调用resume()方法-->");
if (stateName.equals("阻塞状态")) {
hj.setState(new Runnable());
} else {
System.out.println("当前线程不是阻塞状态,不能调用resume()方法.");
}
}
}
//具体状态类:死亡状态
class Dead extends ThreadState {
public Dead() {
stateName = "死亡状态";
System.out.println("当前线程处于:死亡状态.");
}
}
public class Client {
public static void main(String[] args) {
ThreadContext context = new ThreadContext();
context.start();
context.getCPU();
context.suspend();
context.resume();
context.getCPU();
context.stop();
}
}
策略模式(Strategy)
总结:
在一段长代码中 把容易变化的 算法 抽离出来,作为参数传入 就可以考虑此模式
例如 Arrays.sort(T[] a, Comparator<? super T> c)这个排序方法 需要各种比较器作为策略传入
参考例子:策略 - 廖雪峰的官方网站
责任链模式 (Chain of Responsibility)
总结:
一、 在一段使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
例如,JavaEE的Servlet规范定义的Filter就是一种责任链模式,它不但允许每个Filter都有机会处理请求,还允许每个Filter决定是否将请求“放行”给下一个Filter
二、核心角色就是 HandlerChain 实现了链条的处理
public class HandlerChain {
private List<Handler> handlers = new ArrayList<Handler>();
public void addHandler(Handler handler) {
this.handlers.add(handler);
}
public boolean handle(Request request){
for (Handler handler : handlers) {
Boolean r = handler.handle(request);
if(r!=null){
System.out.println(request + " " + (r ? "Approved by " : "Denied by ") + handler.getClass().getSimpleName());
return r;
}
}
throw new RuntimeException("Could not handle request: " + request);
}
}
参考例子:责任链 - 廖雪峰的官方网站