设计模式

七大原则

  • 单一职责原则
  • 接口隔离原则
  • 依赖倒转原则
  • 里氏开闭原则 ocp
  • 迪米特法则
  • 合成复用原则

经典案例

状态模式

一个订单会有多种状态,发布,审核,抢单,付款等等,使用状态模式来设计。

(多种状态,会用很多if else,代码臃肿,不易扩展)

单例模式

写出单例模式的实现方式,代码实现,分别说出里面的优缺点。

饿汉(静态常量)

1
2
3
4
5
6
7
8
9
public class Hungry1 {
private final static Hungry1 hungry1=new Hungry1();
private Hungry1(){

}
public static Hungry1 getInstance(){
return hungry1;
}
}

在类加载的时候就创建实例,会造成内存浪费,不能实现懒加载,实现简单方便

饿汉(静态代码块)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Hungry2 {
private static Hungry2 hungry;
static {
hungry=new Hungry2();
}

private Hungry2(){

}
public static Hungry2 getInstance(){
return hungry;
}
}

在类加载的时候就创建实例,会造成内存浪费,不能实现懒加载,实现简单方便

懒汉(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
public class Lazy1 {
private static Lazy1 instance;
private Lazy1(){

}
public static Lazy1 getInstance(){
if(instance == null)
instance=new Lazy1();
return instance;
}
}

实现了懒加载,但是线程不安全

懒汉(线程安全,同步方法)

1
2
3
4
5
6
7
8
9
10
11
public class Lazy2 {
private static Lazy2 instance;
private Lazy2(){

}
public static synchronized Lazy2 getInstance(){
if(instance == null)
instance=new Lazy2();
return instance;
}
}

线程安全,但是效率低,所有获取实例的时候都是同步操作

饿汉(线程不安全,同步代码块)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Lazy4 {
private static Lazy4 instance;
private Lazy4(){

}
public static Lazy4 getInstance(){
if(instance == null)
synchronized (Lazy4.class){
instance=new Lazy4();
}
return instance;
}
}

线程不安全,多个线程还是会同时进入if代码块里,按顺序依次创建多个实例

懒汉(双重加锁)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Lazy3 {
private static volatile Lazy3 instance;
private Lazy3(){

}
public static Lazy3 getInstance(){
if(instance == null){
synchronized (Lazy3.class){
if(instance == null){
instance=new Lazy3();
}
}
}
return instance;
}
}

线程安全,提高效率,每个线程进来第一次判断为null,才会进入同步代码块,然后第二次判断保证只有一次实例创建(volatile会将更新值立刻刷新到主存)

懒汉(静态内部类)

1
2
3
4
5
6
7
8
9
10
11
12
public class Lazy5 {
private Lazy5(){

}

private static class Lazy5Inner{
private static final Lazy5 instance = new Lazy5();
}
public static Lazy5 getInstance(){
return Lazy5Inner.instance;
}
}

当类加载的时候,静态内部类并不会加载,实现了懒加载,只有调用的时候才会被加载,且类加载的时候是线程安全的,JVM保证类加载的时候,别的线程是无法进入的

懒汉(枚举)

1
2
3
4
5
6
7
8
9
10
11
public class Lazy6 {
public static void main(String[] args) {
Instance instance=Instance.INSTANCE;
Instance instance1=Instance.INSTANCE;
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
enum Instance{
INSTANCE;
}
}

不仅可以避免多线程的问题,也可以避免反序列化重新创建新的对象

使用场景

需要频繁创建和销毁的对象,创建对象耗时过多消费资源过多,但用经常用到的,例如工具类,数据库session工程,数据源等

工厂模式

简单工厂模式

将业务需求抽出,由工厂来完成,当未来业务需要变更的时候,只需要更改工厂的方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author Sun
* @date:11:04 2020/2/14
* @description: 抽象披萨对象
*/
public abstract class Pizza {
private String name;
//需要子类实现
public abstract void prepare();
public void make(){
System.out.println(name+"制作中..");
}
public void ok(){
System.out.println(name+"准备好了..");
}

public void setName(String name) {
this.name = name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author Sun
* @date:11:06 2020/2/14
* @description: 具体披萨实现类
*/
public class ChessePizza extends Pizza {
@Override
public void prepare() {
System.out.println("芝士pizza准备中..");
setName("芝士");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author Sun
* @date:11:08 2020/2/14
* @description: 门店1
*/
public class Store1 {
public void createPizza(String type){
System.out.println("店家1...");
Pizza pizza=null;
if(type.equals("C")){
pizza=new ChessePizza();
}else if(type.equals("P")){
pizza=new PeperPizza();
}
if(pizza==null)
System.out.println("没有你要的披萨");
else {
pizza.prepare();
pizza.make();
pizza.ok();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author Sun
* @date:11:08 2020/2/14
* @description: 门店2
*/
public class Store2 {
public void createPizza(String type){
System.out.println("店家2....");
Pizza pizza=null;
if(type.equals("C")){
pizza=new ChessePizza();
}else if(type.equals("P")){
pizza=new PeperPizza();
}
if(pizza==null)
System.out.println("没有你要的披萨");
else {
pizza.prepare();
pizza.make();
pizza.ok();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @author Sun
* @date:11:12 2020/2/14
* @description: 顾客购买类
*/
public class Customer {
public static void main(String[] args) {
System.out.println("顾客开始来店1买芝士");
new Store1().createPizza("C");
System.out.println("顾客开始来店1买芝士");
new Store2().createPizza("C");

}
}

这是正常的购买流程,但是如果现在需要再加一种售卖的披萨,那么门店1和门店2类,就都要增加一种判断,我们可以使用简单工厂模式,让工厂来接手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author Sun
* @date:11:03 2020/2/14
* @description: 简单工厂
*/
public class Factory {
public static Pizza create(String type){
Pizza pizza=null;
if(type.equals("C")){
pizza=new ChessePizza();
}else if(type.equals("P")){
pizza=new PeperPizza();
}
return pizza;
}
}

工厂方法模式

定义一个创建对象的抽象方法,由子类决定要实例化的类,工厂方法模式将对象的实例化推到子类

1
2
3
4
5
6
7
8
9
10
11
/**
* @author Sun
* @date:11:03 2020/2/14
* @description: 工厂方法模式
*/
public abstract class Factory {
public Pizza getPizza(String type){
return createPizza(type);
}
abstract Pizza createPizza(String type);
}

抽象工厂模式

定义一个接口用于创建相关或依赖关系的对象簇,简单点说将工厂类作为接口,让子类去实现。

遵循依赖抽象原则

  • 创建对象实例时,不要直接new类,而是把这个new类的动作放在一个工厂的方法中,并返回
  • 不要让类继承具体类,而是继承抽象类或者是接口
  • 不要覆盖基类已经实现的方法。

原型模式

  • 原型模式是指:用原型实例指定创建对象的种类,并通过拷贝这些原型,创建新的对象。
  • 原型模式是一种创建型设计模式,允许一个对象在创建另一个可以定制的对象,无需知道如果创建的细节
  • 工作原理:通过将一个原型对象.clone()

浅拷贝和深拷贝

  • 浅拷贝:对于数据类型是基本数据类型的成员变量,浅拷贝会直接复制一份到新的对象,对于数据类型是引用类型,例如数组,对象,则只会复制一份引用内存地址,并不会真的一份对象,所以复制出来的对象的引用类型成员变量都是指向同一个地址。
  • 深拷贝:重写Clone来是实现,通过对象序列化

构造者模式

构造者的四个角色

  • 产品角色
  • builder抽象的建造者
  • concreteBuilder具体建造者
  • director指挥者