在学习设计模式前,我们先要弄明白一个问题,那就是为什么要学习设计模式?在JAVA领域,大部分使用的是贫血模型,业务逻辑都是service之间的调用,类似于函数式编程,其他高深的东西都封装于框架中,平时能用上设计模式的地方确实不多,似乎学习了也没啥用。
我觉得设计模式特别像九阳神功,练成「九阳神功」后,内力自生速度奇快,无穷无尽,普通拳脚也能使出绝大攻击力,学习了设计模式,虽然还是普通的功能,却在稳定性,可维护性上大不相同了。
九阳神功是融会贯通武学体系,练成后天下武学皆附拾可用。学会了设计模式,再看各种开源框架源码,就能融会贯通,明白作者为什么要这么写,并且为已所用。
设计模式总览
七大设计原则
开闭原则,软件实体应当对扩展开放,对修改关闭,扩展新类而不是修改旧类
里氏替换原则,继承必须确保超类所拥有的性质在子类中仍然成立,继承父类而不去改变父类
依赖倒置原则,高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,面向接口编程,而不是面向实现类
单一职责原则,一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分,每个类只负责自己的事情,而不是变成万能
接口隔离原则,一个类对另一个类的依赖应该建立在最小的接口上,各个类建立自己的专用接口,而不是建立万能接口
迪米特法则,只与你的直接朋友交谈,不跟“陌生人”说话,无需直接交互的两个类,如果需要交互,使用中间者
合成复用原则,软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
单例模式
一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
单例特点
某个类只能有一个实例;(构造器私有)
它必须自行创建这个实例;(自己编写实例化逻辑)
它必须自行向整个系统提供这个实例;(对外提供实例化方法)
使用场景
多线程中的线程池
数据库的连接池
系统环境信息
上下文(ServletContext) ......
接下去看下经典的懒汉模式的单例代码
public class SingleObject {//使用volatile保证内存可见性,防止指令重排private volatile static SingleObject instance;//私有构造器,外部无法newprivate SingleObject() {}public static SingleObject getInstance() {//判断是否已有实例,没有则创建实例if (instance == null) {//多线程情况下需要加锁synchronized (SingleObject.class) {//防止多个线程走到这里,所以还需要判断一次if (instance == null) {instance = new SingleObject();}}}return instance;}
}
工厂模式
提供了一种创建对象的最佳方式。我们不必关心对象的创建细节,只需要根据不同情况获取不同产品即可。难点:写好我们的工厂
简单工厂
简单工厂的核心代码就是MouseFactory的createMouse,通过传入不同的type来返回不同的Mouse
public abstract class Mouse {public abstract void sayHi();public void test() {System.out.println("测试工作");}
}public class DellMouse extends Mouse {@Overridepublic void sayHi() {System.out.println("DellMouse");}
}public class HpMouse extends Mouse {@Overridepublic void sayHi() {System.out.println("HpMouse");}
}public class SimpleMouseFactory {//创建mouse对象public static Mouse createMouse(int type) {Mouse mouse = null;if (type == 0) {mouse = new DellMouse();} else if (type == 1) {mouse = new HpMouse();} else {throw new IllegalArgumentException();}//出厂测试mouse.test();return mouse;}@Testpublic void testCreate() {Mouse mouse = createMouse(0);mouse.sayHi();mouse = createMouse(1);mouse.sayHi();}
}
现在思考一个问题,为什么要用工厂模式,而不是直接new一个HpMouse或者DellMouse呢?
首先,根据依赖倒置原则,我们应该依赖抽象,而不是具体的类,直接new一个就是依赖于具体的类了。其次工厂模式可以在产品出厂前做一些特殊处理,比如测试工作,这个很重要,Spring的很多重要功能都依赖于此。我们知道Spring里面有个bean工厂,而它生产的不是原始对象,而是代理对象,只有通过代理对象才能实现面向切面编程,后面的拦截器,事务处理等才有了实现的可能。理解了上面两点,也就理解了工厂模式。
简单工厂有个缺点就是如果添加新的产品,需要修改createMouse方法,也就违反了开闭原则。这个时候就可以使用工厂模式。
工厂模式
工厂模式就是把MouseFactory作为抽象类,每一种产品都新建一个工厂类,一个工厂类只生成一种产品,这样当有新的产品时,只需要新建一个工厂类就行了。
public abstract class MouseFactory {public abstract Mouse createMouse();static class HpMouseFactory extends MouseFactory {@Overridepublic Mouse createMouse() {return new HpMouse();}}static class DellMouseFactory extends MouseFactory {@Overridepublic Mouse createMouse() {return new DellMouse();}}public static void main(String[] args) {HpMouseFactory hpMouseFactory = new HpMouseFactory();DellMouseFactory dellMouseFactory = new DellMouseFactory();Mouse mouse = hpMouseFactory.createMouse();mouse.sayHi();mouse = dellMouseFactory.createMouse();mouse.sayHi();}
}
抽象工厂模式
工厂模式有个限制就是只能生产一类的产品,如果想又生产鼠标又生产键盘该怎么办呢?这时就要用到抽象工厂了,把生产鼠标和键盘的方法放在抽象工厂类中,下面的实现类来决定具体生产哪一种产品。
public abstract class PcFactory {abstract Mouse createMouse();abstract Keybo createKeybo();static class HpFactory extends PcFactory {@OverrideMouse createMouse() {return new HpMouse();}@OverrideKeybo createKeybo() {return new HpKeybo();}}static class DellFactory extends PcFactory {@OverrideMouse createMouse() {return new DellMouse();}@OverrideKeybo createKeybo() {return new DellKeybo();}}public static void main(String[] args) {DellFactory dellFactory = new DellFactory();Mouse mouse = dellFactory.createMouse();mouse.sayHi();Keybo keybo = dellFactory.createKeybo();keybo.sayHi();}
}