设计模式
软件设计原则
开闭原则
对扩展开放,对修改关闭
适合用抽象类和接口实现
里氏代换原则
- 任何基类出现的地方,子类一定可以出现
- 子类可扩展但不能改变父类原有功能,就是尽量不重写父类方法
反例
假设有个长方形类,正方形类继承长方形类,同时自己内部重写了长和宽的 setter 方法,之后假设有个 resize 方法拓宽长方形,长方形实例没
问题,但是正方形实例 resize 方法就会同时拓宽长和宽,违反里氏代换原则
解决方法
定义一个四边形接口,正方形类和长方形类都实现这个接口,这样长方形和正方形就没有继承关系了,符合里氏代换原则
依赖倒置原则
- 高层模块不应该依赖低层模块,两者都应该依赖抽象
- 抽象不应该依赖细节,细节应该依赖抽象(细节就是具体实现类)
反例
定义一个电脑类,电脑有 CPU,内存,硬盘等,电脑直接依赖希捷硬盘,英特尔 CPU,金士顿内存条,之后如果用别的牌子的设备就要对电脑类的 setter 方法里面的类 进行修改
解决方法
定义 CPU,内存,硬盘等接口,所有牌子的设备实现对应种类的接口,电脑类的 setter 方法传入接口,这样电脑类就只依赖抽象,不依赖具体实现类,符合依赖倒置原则
接口隔离原则
- 客户端不应该被迫依赖他不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上
- 就假设一个类实现了接口,接口有方法 1 和方法 2,但这个类并不需要方法 2,所以应该给两个方法分别都写成接口,类需要哪个就实现哪个接口
反例
假设有个安全门接口,有三个功能防盗、防火、防水,A 类安全门功能都需要直接实现就行,但是 B 类安全门只需要防盗防火功能,他不需要实现防水,被迫依赖了
解决方法
给三个功能全抽成接口,A 类安全门实现三个接口,B 类安全门只实现两个接口
迪米特法则(最少知识原则)
只与你的直接朋友交谈,不和陌生人说活
其含义是:如果两个软件实体无需直接通信,那么就不应当发生直接的相互调用,而是通过第三方转发
例子
假设有明星类、粉丝类、公司类
建立一个经纪人类。前三个类都是经纪人类的成员变量,分别有对这三个类的方法,就能降低耦合度
合成复用原则
指尽量使用组合、聚合的方式,其次才考虑继承
组合和聚合就是使用成员变量的方式
组合是强依赖,由整体对象作为部分创建出来,整体对象销毁时也随之销毁(独占)
聚合是弱依赖,由外部创建后引入进来(共享)
单一职责原则
一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分
创建者模式
单例模式
该类负责创建自己的对象,同时确保只有单个对象被创建,并提供一个访问其唯一对象的方式
单例设计模式分类两种:
饿汉式:类加载的时候创建对象
懒汉式:类加载的时候不创建对象,而是第一次调用的时候创建对象
饿汉式单例实现方式
- 静态成员变量
private Singleton () {};// 私有构造方法,不写的话会用默认构造器,默认是 public 的private static Singleton instance = new Singleton();
- 静态代码块
private Singleton () {};private static Singleton instance;static {instance = new Singleton();}
懒汉式单例实现方式
懒汉式实现方式
1 | |
双重检查锁方式
读不加锁提升性能
但是有可能出现空指针问题,因为 JVM 的优化和指令重排序
用 volatile 修饰单例,保证可见性和有序性
1 | |
静态内部类方式(常用)
由于 JVM 在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性/方法被调用时才会被加载,并初始化其静态属性。静态属性由于被static修饰,保证只实施例换一次,并严格保证实例化顺序
在没加锁的情况下保证线程安全,并且没有任何性能影响和空间浪费
1 | |
枚举方式(最推荐)(属于饿汉式,在不考虑内存浪费的时候最推荐)
线程安全,而且只会加载一次,而且是所有单例实现里面唯一不会被破坏的
1 | |