在学习设计模式前,我们先要弄明白一个问题,那就是为什么要学习设计模式?在JAVA领域,大部分使用的是贫血模型,业务逻辑都是service之间的调用,类似于函数式编程,其他高深的东西都封装于框架中,平时能用上设计模式的地方确实不多,似乎学习了也没啥用。
我觉得设计模式特别像九阳神功,练成「九阳神功」后,内力自生速度奇快,无穷无尽,普通拳脚也能使出绝大攻击力,学习了设计模式,虽然还是普通的功能,却在稳定性,可维护性上大不相同了。
九阳神功是融会贯通武学体系,练成后天下武学皆附拾可用。学会了设计模式,再看各种开源框架源码,就能融会贯通,明白作者为什么要这么写,并且为已所用。
设计模式总览
七大设计原则
开闭原则,软件实体应当对扩展开放,对修改关闭,扩展新类而不是修改旧类
里氏替换原则,继承必须确保超类所拥有的性质在子类中仍然成立,继承父类而不去改变父类
依赖倒置原则,高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象,面向接口编程,而不是面向实现类
单一职责原则,一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分,每个类只负责自己的事情,而不是变成万能
接口隔离原则,一个类对另一个类的依赖应该建立在最小的接口上,各个类建立自己的专用接口,而不是建立万能接口
迪米特法则,只与你的直接朋友交谈,不跟“陌生人”说话,无需直接交互的两个类,如果需要交互,使用中间者
合成复用原则,软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
单例模式
一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。
单例特点
某个类只能有一个实例;(构造器私有)
它必须自行创建这个实例;(自己编写实例化逻辑)
它必须自行向整个系统提供这个实例;(对外提供实例化方法)
使用场景
多线程中的线程池
数据库的连接池
系统环境信息
上下文(ServletContext) ......
接下去看下经典的懒汉模式的单例代码
public class SingleObject {
//使用volatile保证内存可见性,防止指令重排
private volatile static SingleObject instance;
//私有构造器,外部无法new
private 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 {
@Override
public void sayHi() {
System.out.println("DellMouse");
}
}
public class HpMouse extends Mouse {
@Override
public 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;
}
@Test
public 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 {
@Override
public Mouse createMouse() {
return new HpMouse();
}
}
static class DellMouseFactory extends MouseFactory {
@Override
public 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 {
@Override
Mouse createMouse() {
return new HpMouse();
}
@Override
Keybo createKeybo() {
return new HpKeybo();
}
}
static class DellFactory extends PcFactory {
@Override
Mouse createMouse() {
return new DellMouse();
}
@Override
Keybo 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();
}
}