知识要点
-
六大设计原则(也有叫七大设计原则)
-
23种设计模式
设计原则
1、开闭原则OCP(Open Close Principle)
对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。使程序的扩展性好,易于维护和升级。
2、里氏替换原则LSP(Liskov Substitution Principle)
所有使用父类的地方可以使用子类的对象,子类可以扩展父类的功能,但是不能替换父类的的功能。如果需要替换父类功能,建议——多用组合,少用继承, 已经有的类和功能直接拿来组合复用(合成复用原则CRP(Composite Reuse Principle), 这里引申出来, 没有单拿出来)。
里氏代换原则是面向对象设计的基本原则之一, 主要针对继承来说, 是继承复用的基石,是对开闭原则的补充。(实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范)
3、依赖倒转原则DIP(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:针对接口编程,依赖于抽象接口而不依赖于具体实现。改变原有自上而下的依赖方向。
4、接口隔离原则ISP(Interface Segregation Principle)
建立单一接口,不要建立臃肿庞大的接口。接口尽量细化,同时接口中的方法尽量少, 不要违反单一职责原则;
提高接口、类、模块的处理能力,减少对外的交互。(降低耦合)
通过对不同高质量接口的组装,实现服务功能的定制化。
5、迪米特法则/最少知道原则DP(Demeter Principle)
一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。调用方只想通过得到自己想要的功能和结果, 不关心被调用方内部如何复杂如何实现.
6、单一职责原则SRP:(Single Reposibility Principle)
一个类或者模块只负责完成某一个单一的功能职责。
设计模式
设计模式一共有23种,分为三大类: 创建型, 结构型, 行为型; 常用的: 单例, 工厂, 代理, 适配器.
创建型设计模式
创建型模式的主要关注点是“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
1.单例(Singleton)
某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,节省内存, 其拓展是有限多例模式。
五种实现方式:
- 饿汉式
/**
* @description 饿汉式(线程安全,调用效率高,但是不能延时加载)
**/
public class SingletonDemo1 {
private static SingletonDemo1 instance = new SingletonDemo1();
private SingletonDemo1(){}
public static SingletonDemo1 getInstance(){
return instance;
}
}
- 懒汉式
/**
* @description 懒汉式(线程安全,调用效率不高,但是能延时加载)
**/
public class SingletonDemo2 {
// 类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)
private static SingletonDemo2 instance;
// 构造器私有化
private SingletonDemo2(){}
// 方法同步,调用效率低
public static synchronized SingletonDemo2 getInstance() {
if(instance == null) {
instance = new SingletonDemo2();
}
return instance;
}
}
- 双重校验(双重判定锁)
/**
* @description 双重校验(双重判定)
**/
public class SingletonDemo3 {
private volatile static SingletonDemo3 singletonDemo3;
private SingletonDemo3() {}
public static SingletonDemo3 newInstance() {
if (singletonDemo3 == null) {
synchronized (SingletonDemo3.class) {
if (singletonDemo3 == null) {
singletonDemo3 = new SingletonDemo3();
}
}
}
return singletonDemo3;
}
}
- 静态内部类
/**
* 问题:为什么这种内部静态类的方式,是线程安全的?
* 答:首先要了解类加载过程中的最后一个阶段:即类的初始化,类的初始化阶本质就是执行类构造器的<clinit>方法。那么什么是<clinit>方法?
* 这不是由程序员写的程序,而是根据代码由javac编译器生成的。它是由类里面所有的【静态成员的的赋值语句】和【静态代码块】组成的。JVM内部会保证一个类的
* <clinit>方法在多线程环境下被正确的加锁同步,也就是说如果多个线程同时去进行“类的初始化”,那么只有一个线程会去执行类的<clinit>方法,其他的线程
* 都要阻塞等待,直到这个线程执行完<clinit>方法。然后执行完<clinit>方法后,其他线程唤醒,但是不会再进入<clinit>方法。也就是说同一个加载器下,
* 一个类型只会初始化一次。
*
* 那么回到这个代码中,这里的静态变量的赋值操作进行编译之后实际上就是一个<clinit>代码,当我们执行getInstance方法的时候,会导致SingletonClassInstance
* 类的加载,类加载的最后会执行类的初始化,但是即使在多线程情况下,这个类的初始化的<clinit>代码也只会被执行一次,所以他只会有一个实例。
*
* @description 静态内部类(线程安全,调用效率高,可以延时加载)
**/
public class SingletonDemo4 {
private SingletonDemo4(){
System.out.println("SingletonDemo4");
}
/** 静态内部类 */
private static class SingletonClassInstance {
private static final SingletonDemo4 instance = new SingletonDemo4();
}
/** 只有在第一次调用时,才会被创建,可以认为是懒加载的升级版本 */
public static SingletonDemo4 getInstance(){
return SingletonClassInstance.instance;
}
public static void main(String[] args) {
System.out.println("args = " + Arrays.deepToString(args));
SingletonDemo4.getInstance();
SingletonDemo4.getInstance();
}
}
- 枚举类(本身就是单例)
2.原型(Prototype)
将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例, 不通过new关键字来产生对象。内存中二进制流的拷贝, 性能好于new.
使用场景:
① 类的初始化需要消耗非常多的资源。
② 通过new产生一个对象需要非常繁琐的数据准备或访问。
③ 一个对象需要提供给其他对象访问,而且每个调用者可能都需要修改其值时。
引申:浅拷贝与深拷贝
3.简单工厂
一个工厂类,对实现了同一接口的一些类进行实例的创建,适合创建对象比较少的场景;相对简单,但是违反开闭原则, 一旦类修改或新增类,就要修改工厂, 耦合度高。
简单工厂其实不像是一个设计模式,反而比较像是一种编程习惯.
public class SimplePizzaFactory {
public Pizza createPizza(String pizzaType) {
Pizza pizza = null;
if (pizzaType.equals("cheese")) {
pizza = new CheesePizza();
} else if (pizzaType.equals("pepperoni")) {
pizza = new PepperoniPizza();
} else if (pizzaType.equals("clam")) {
pizza = new ClamPizza();
} else if (pizzaType.equals("veggie")) {
pizza = new VeggiePizza();
}
return pizza;
}
}
4.工厂方法(Factory Method)
定义了一个创建对象的抽象方法,若干个子工厂类实现接口, 每一个子工厂类固定创建指定的类, 把实例化推迟到子类。
5.抽象工厂(Abstract Factory)
提供一个创建产品族的接口,而不需要明确指定要创建的具体类, 其每个子类可以生产一系列相关的产品。
6.建造者/生成器模式(Builder)
将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。@Builder注解
结构型设计模式
结构型模式描述如何将类或对象按某种布局组成更大的结构。分为类结构型模式(采用继承机制来组织接口和类)和对象结构型模式(釆用组合或聚合来组合对象)。
1.代理(Proxy)
为某对象提供一种代理以控制对该对象的访问。即:通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性, 降低耦合。Spring AOP中采用的JDK动态代理实现事务控制.
2.适配器(Adapter)
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作, 保证兼容性。(抽象类实现接口, 子类继承抽象类自定义实现需要方法.)
3.桥接(Bridge)
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。Has-A状态
4.装饰(Decorator)
动态地给对象增加一些职责,即:增加其额外的功能。
5.门面(Facade)
为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。 • 享元(Flyweight):运用共享技术来有效地支持大量细粒度对象的复用。
6.组合(Composite)
将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
行为型设计模式
行为型模式用于描述程序在运行时复杂的流程控制,即:描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式(采用继承机制来在类间分配行为)和对象行为模式(采用组合或聚合在对象间分配行为)。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
行为型模式是GoF设计模式中最为庞大的一类.
1.模板方法(Template Method)
定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
2.策略(Strategy)
定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
3.命令(Command)
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
4.职责链(Chain of Responsibility)
把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
5.状态(State)
允许一个对象在其内部状态发生改变时改变其行为能力。
6.观察者(Observer)
多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
7.中介者(Mediator)
定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
8.迭代器(Iterator)
提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
9.访问者(Visitor)
在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
10.备忘录(Memento)
在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
11.解释器(Interpreter)
提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。