3万字聊聊设计模式
Posted Leo聊技术
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3万字聊聊设计模式相关的知识,希望对你有一定的参考价值。
大家好,我是Leo。
之前聊过了 MySQL 、Redis、RocketMQ、秒杀系统、计算机网络
由于现阶段工作规划的问题,计算机网络暂时放放,先聊一下设计模式。
七大原则
单一职责原则
对类来说,即一个 类应该只负责一项职责,如类A负责两个不同的职责,我们把职责分为职责1和职责2。当职责1需要变更的时候,我们修改的类A,可能会造成职责2执行错误。
真实场景:货主端的新增司机就应该只是新增司机,即使从类上做不到单一,最起码的底线是要做到方法上的单一。即使当下没有这个需求也不可以。也要考虑扩展性,可读性!
为我们解决了什么
- 降低类的复杂度,一个类只负责一项职责
- 提高类的可读性,可维护性
- 降低变更引起的风险
- 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
接口隔离原则
客户端不应该依赖它不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上。
用不到的接口不要实现,可以用抽象解决这一问题。或者定义多个接口解决这一问题
为我们解决了什么
- 单一化接口的职责,从而有效地避免接口污染。
- 当一个接口的方法过多,往往会造成使用该接口的类中闲置一些方法,造成代码的冗余,通过细分接口可有效避免该现象。
- 可以提高代码的灵活性,就好比搭积木一样,我们可以将一个大的接口拆成多个小接口,不同的小接口可以有多种组合。
- 促使程序高内聚、低耦合。
依赖倒转原则
- 高层模块不应该依赖低层模块,二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 依赖倒转的中心思想是面向接口编程
- 依赖倒转原则是基于这样的设计理念;相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在Java中,抽象指的是接口或抽象类,细节就是具体的实现类
- 使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
为我们解决了什么
- 可以减少类间的耦合性、提高系统稳定性,提高代码可读性和可维护性,可降低修改程序所造成的风险。
里氏替换原则
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法。继承实际上是让两个类的耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题(基类)
为我们解决了什么
- 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
- 提高代码的重用性;
- 子类可以形似父类,但又异于父类,“龙生龙,凤生凤,老鼠生来会打洞”是说子拥有父的“种”,“世界上没有两片完全相同的叶子”是指明子与父的不同;
- 提高代码的可扩展性,实现父类的方法就可以“为所欲为”了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;
- 提高产品或项目的开放性。
需要我们注意什么
- 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;
- 降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;
- 增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果————大段的代码需要重构。
开闭原则
- 一个软件实体如类,模块,函数应该对扩展开放,对修改关闭。用抽象构建框架,对实现扩展细节。
- 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
- 编程中遵循其他原则以及使用设计模式的目的就是遵循开闭原则
迪米特法则
- 一个对象应该对其他对象保持最少的了解(最少知道原则)
- 类与类关系越密切,耦合度越大
- 对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public方法,不对外泄露任何信息
- 只与直接的朋友通信(B类的调用,局部变量B只能算间接朋友)
局部变量不应该出现
合成复用原则
一个类使用另一个类的代码,尽量不使用继承,而是合成
可以通过传递类或者set类的方式,通过这个类名调用,而不是继承
设计模式
设计模式类型
创建型模式
单例模式,抽象工厂模式,原型模式,建造者模式,工厂模式
结构型模式
适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式
行为型模式
模板方法模块,命令模式,访问者模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器模式,状态模式,策略模式,职责链模式(责任链模式)
单例模式
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
方式
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
应用场景
- 需要频繁创建和销毁的对象
- 创建对象时耗时过多或耗费资源过多,但又经常用到的对象,工具类对象,频繁范围数据库或文件的对象
饿汉式(静态常量)
public static void main(String[] args)
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2)
class Singleton
private Singleton()
private final static Singleton instance = new Singleton();
public static Singleton getInstance()
return instance;
优点
- 写法简单,类装载时完成实例化,避免线程同步问题
缺点
- 在类装载的时候完成了实例化,没有达到Lazy Loading的效果,如果从始至终从未使用过这个实例,则会造成内部的浪费
结论
- 饿汉式(静态常量)可以使用,可能会造成内存浪费
饿汉式(静态代码块)与饿汉式(静态常量) 类型
懒汉式(线程不安全)
public static void main(String[] args)
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2)
class Singleton
private static Singleton instance;
private Singleton()
public static Singleton getInstance()
if (instance == null)
instance = new Singleton();
return instance;
优点
- 起到了Lazy Loading的效果,但是只能在单线程下使用
缺点
- 存在系统安全性问题,并发情况下,容易创建多个实例
结论
- 在单线程可以使用,多线程不能使用
懒汉式(线程安全)
public static void main(String[] args)
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2)
class Singleton
private static Singleton instance;
private Singleton()
public static synchronized Singleton getInstance()
if (instance == null)
instance = new Singleton();
return instance;
优点
- 解决了线程不安全问题
缺点
- 效率太低了,每个线程在想获得类的实例时候,执行getInstance方法都要进行同步,而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低
结论
- 在实际开发中,不推荐使用这种方式
懒汉式(线程安全,双重检查)
public static void main(String[] args)
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2)
class Singleton
private static volatile Singleton instance;
private Singleton()
public static synchronized Singleton getInstance()
if (instance == null)
synchronized (Singleton.class)
if (instance == null)
instance = new Singleton();
return instance;
优点
- 线程安全,延迟加载,效率高
结论
- 在实际开发中,推荐使用这种单例模式
静态内部类方式创建
public static void main(String[] args)
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2)
class Singleton
private static volatile Singleton instance;
private Singleton()
private static class SingletonInstance
private static final Singleton INSTANCE = new Singleton();
public static synchronized Singleton getInstance()
return SingletonInstance.INSTANCE;
- 这种方式采用了类装载机制来保证初始化实例时只有一个线程
- 被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化
- 类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮我们保证了线程的安全性,在类初始化时,别的线程是无法进入的。
优点
- 避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
结论
- 推荐使用
枚举方式创建
public static void main(String[] args)
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance1 == instance2)
enum Singleton
INSTANCE;
public void sayOk()
System.out.println("sayOk");
优点
- 通过借助jdk1.5中添加的枚举实现单例模式,不仅能避免多线程同步问题,而且还能防止发序列化重新创建新的对象
结论 :推荐使用
总结
了解设计模式的七大原则,首先介绍了23种设计模式的的单例模式,从众多单例模式的写法中推荐使用 懒汉式(线程安全,双重检查),静态内部类方式,枚举方式创建。不仅保证系统安全问题,还能解决无效占用内存。
下一篇聊聊设计模式中的工厂模式,工厂模式分两种:抽象工厂模式 和 简单工厂模式
以上是关于3万字聊聊设计模式的主要内容,如果未能解决你的问题,请参考以下文章