前言
每周一更,这周开始和大家一起学习设计模式。
谈谈设计原则
设计原则是很重要的一个东西,大厦之于地基,它是我们编码要牢记在心的一个东西。
- 单一职责原则
单一职责指的是一个接口只干一件事情嘛?显然这样理解就太片面了,职责单一应该说的是只对一种事情负责。那改变我们的接口的原因只有一个,当负责的那件事情发生改变我们接口也相对应的改变,即一个接口支撑一个业务,当该业务改变,接口随着改变。 - 开闭原则
当我们去做功能增强或者优化的时候我们得想到这个原则了,对拓展开放,对修改关闭。当我们去增强时,尽量不去修改别人的代码,而是用继承等方式增加类或者增加方法的方式进行增强。 - 接口隔离职责
接口隔离我的理解是根据业务场景最小粒度的去拆分接口, - 里式替换原则
里氏替换指的是子类能完全的替换父类,替换后对应的功能没有发生改变。这样是为了在继承父类时,避免子类重写父类,导致方法调用的混乱。所以设计父类尽量没有具体实现。 - 依赖倒置原则
依赖倒置是指我们在方法的入参处,我们需要依赖抽象类而不是具体类。越抽象越稳定,我们需要依赖稳定,同时也方便后续的拓展。 - 迪米特原则
迪米特也叫最少知识原则,即用户调用接口时需要知道的知识越少越好。倘若一个接口调用需要读一本说明书,那这个接口肯定是不符合这个原则的。可以将一些参数、常量封装在类内部、使用构造器等方式,在创建对象的时候就拥有了这些知识。也解释为只与直接朋友通信。类的属性、方法的参数与返回值称作类的直接关联对象,而类中的方法则成非直接关联对象,所以当出现依赖非直接关联对象我们就得考虑将方法拆分。 - 合成复用原则
合成复用是指尽量使用合成、聚合的方式依赖,而不使用继承。继承的耦合性高于合成/聚合的方式,当我们用不到父类的一些方法时,也继承到了子类。其二java中只能单继承,所以尽量不使用继承。
单例模式
单例模式一般都是大家学的第一个设计模式。当我们系统中某个东西只能有一个的时候(比如建立目录 数据库连接都需要这样的单线程操作),或者是和实例无关使用单例来节约资源(例如springBoot中bean的创建) java中有很多种方式会破坏单例,于是就有为了解决各种破坏单例写法。饿汉式、懒汉式、双重检查、静态内部类、枚举。
如果该对象一定会使用到,那使用饿汉式一点问题没有。
private static Singleton INSETANCE = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return INSETANCE;
}
创建两个对象进行对比结构返回ture,表明是同一个对象
当单例实现后就会有人想,要是没用到这个对象,岂不是就会浪费资源。是的,于是有了懒汉式,当调用的时候才创建,即在上面getInstance中不直接return了,判断Singleton是否为空,为空则创建,否则就返回Singleton对象。此刻又有高手提出疑问,当多线程时那这单例就彻底失效了,会有多个对象创建出来。答曰:加锁嘛,synchronized 一套就好了。于是就有了双重检查方式创建单例。
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance == null){
synchronized (Singleton.class){
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
【注意】记得保证instance的原子性,需要使用volatile 修饰
静态内部类也能实现单例模式
public class Singleton {
private Singleton() {
}
private static class SingletonInstance{
private static Singleton singleton = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.singleton;
}
}
其实核心还是构造私有禁止直接创建对象,线程同步和避免资源的浪费都由这个内部类来帮我们完成。静态内部类只有当使用的时候才加载,这时候才会创建Singleton,虚拟机类加载时是同步操作,所以这是个比较完美的单例。 【注】虚拟机类加载等在后续JVM章节
接下来该到我们jdk1.5的神器-枚举出场了,实现单例的最优解
public enum Singleton {
INSTANCE;
}
是的,就这样实现了单例,同时它还避免了反序列化和反射重新创建对象
public static void main(String[] args) {
Singleton singleton1 = Singleton.INSTANCE;
Singleton singleton2 = Singleton.INSTANCE;
System.out.println(singleton1 == singleton2);
}
后续
后续应该是每周一个设计模式的方式进行更新,所以有同样想学设计模式的同学可以跟着我一起。
|