设计模式 Design pattern
设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、提高代码可读性、保证代码可靠性。
七大原则
1、开闭原则
一个软件实体如类,模块和函数应该对扩展开放,对修改关闭。用抽象构建框架,用实现扩展细节。
2、里氏代换原则
继承必须确保超类所拥有的性质在子类中仍然成立。 通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能
3、依赖倒置原则
高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。 其核心思想是:要面向接口编程,不要面向实现编程。
4、单一职责原则
职责是指类变化的原因,单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。 单一职责原则是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。
5、接口隔离原则
要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。
6、最少知道原则(迪米特法则)
如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。 其目的是降低类之间的耦合度,提高模块的相对独立性
从迪米特法则的定义和特点可知,它强调以下两点:
- 从依赖者的角度来说,只依赖应该依赖的对象。
- 从被依赖者的角度说,只暴露应该暴露的方法。
7、合成复用原则
在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
总结
| 设计原则 | 一句话归纳 | 目的 |
|---|---|---|
| 开闭原则 | 对扩展开放,对修改关闭 | 降低维护带来的新风险 |
| 依赖倒置原则 | 高层不应该依赖低层,要面向接口编程 | 更利于代码结构的升级扩展 |
| 单一职责原则 | 一个类只干一件事,实现类要单一 | 便于理解,提高代码的可读性 |
| 接口隔离原则 | 一个接口只干一件事,接口要精简单一 | 功能解耦,高聚合、低耦合 |
| 迪米特法则 | 不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度 | 只和朋友交流,不和陌生人说话,减少代码臃肿 |
| 里氏替换原则 | 不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义 | 防止继承泛滥 |
| 合成复用原则 | 尽量使用组合或者聚合关系实现代码复用,少使用继承 | 降低代码耦合 |
| 目的只有一个:降低对象之间的耦合,增加程序的可复用性、可扩展性和可维护性。 |
三大分类
- 创建型模式: 对象实例化的模式,创建型模式用于解耦对象的实例化过程
- 结构型模式: 把类和对象结合在一起形成一个更大的结构
- 行为型模式: 类的对象如何交互,及划分责任和算法
常用模式
工厂方法模式、抽象工厂模式、单例模式、代理模式
工厂方法模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,也就是说工厂方法模式让实例化推迟到子类。
抽象工厂模式
所谓抽象工厂模式就是提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。他允许客户端使用抽象的接口来创建一组相关的产品,而不需要关系实际产出的具体产品是什么
单例模式
指一个类只有一个实例,且该类能自行创建这个实例的一种模式 懒汉式单例:
public class LazySingleton {
// 证 instance 在所有线程中同步
private static volatile LazySingleton instance = null;
private LazySingleton() {
} // rivate 避免类在外部被实例化
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
饿汉式单例:
public class HungrySingleton {
rivate static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
静态内部类:
public class SignleObject4 {
private static class SignletonHolder {
public static SignleObject4 instance = new SignleObject4();
}
private SignleObject4() { }
public static SignleObject4 getInstance() {
return SignletonHolder.instance;
}
}
枚举:
class Resource{
}
public enum SomeThing {
INSTANCE;
private Resource instance;
private SomeThing() {
instance = new Resource();
}
public Resource getInstance() {
return instance;
}
}
代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
动态代理示例:
// 辅导班类 JdkFuDao
public class JdkFuDao implements InvocationHandler {
private IPerson target;
public IPerson getInstance(IPerson target) {
this.target = target;
Class<?> clazz = target.getClass();
return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(this.target, args);
after();
return result;
}
private void after() {
System.out.println("双方同意,开始辅导");
}
private void before() {
System.out.println("这里是C语言中文网辅导班,已经收集到您的需求,开始挑选老师");
}
}
// 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
public class ZhaoLiu implements IPerson {
@Override
public void findTeacher() {
System.out.println("符合赵六的要求");
}
}
// 执行
public class Test {
public static void main(String[] args) {
JdkFuDao jdkFuDao = new JdkFuDao();
IPerson zhaoliu = jdkFuDao.getInstance(new ZhaoLiu());
zhaoliu.findTeacher();
}
}
应用场景
| 模式 | 应用场景 | 例 |
|---|---|---|
| 单例模式 | 希望在系统中某个类的对象只能存在一个 | 网站计数器、数据库连接池 |
| 工厂模式 | 在编码时不能预见需要创建哪种类的实例 | 发送邮件和短信(共同特征:发送信息) |
| 代理模式 | 无法或不想直接引用某个对象或访问某个对象存在困难时 | AOP、日志、Spring事务、全局捕获异常 |