Loading

设计模式

设计模式的SOLID原则

这篇文章主要会介绍几种常见的设计模式,以帮助更好的应对面试中可能会遇到的有关设计模式的问题。设计模式中有一个SOLID原则,分别是单一原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。遵循这5个原则可以使程序做到高内聚,低耦合,更加健壮。

  • 单一责任原则:一个类或者一个方法只做一件事,降低耦合。

  • 开放封闭原则:一个类或者接口应该对扩展开放,对修改关闭。

    这里写图片描述

  • 里氏替换原则:子类可以扩展父类的功能,但是不能改变父类原有的功能。

  • 接口隔离原则:类不应该依赖不需要的接口。

  • 依赖倒置原则:高级模块不应该依赖低级模块,而是依赖抽象。抽象不能依赖细节,而是细节需要依赖抽象。

1.简单工厂模式

简单工厂模式通常就是一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。

public class FoodFactory {
    public static Food makeFood(String name) {
        if (name.equals("noodle")) {
            Food noodle = new LanZhouNoodle();
            noodle.addSpicy("more");
            return noodle;
        } else if (name.equals("chicken")) {
            Food chicken = new HuangMenChicken();
            chicken.addCondiment("potato");
            return chicken;
        } else {
            return null;
        }
    }
}

2.工厂模式

如果在创建对象的时候需要使用两个或者两个以上的工厂,就需要用到工厂设计模式。工厂设计模式是在简单工厂设计模式的基础上的再一次抽象。如下,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food。工厂模式将对象的创建过程和对象的使用方法进行隔离,实现了解耦合。

public interface FoodFactory {
    Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {

    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new ChineseFoodA();
        } else if (name.equals("B")) {
            return new ChineseFoodB();
        } else {
            return null;
        }
    }
}
public class AmericanFoodFactory implements FoodFactory {

    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new AmericanFoodA();
        } else if (name.equals("B")) {
            return new AmericanFoodB();
        } else {
            return null;
        }
    }
}

在客户端调用时:

public class APP {
    public static void main(String[] args) {
        // 先选择一个具体的工厂
        FoodFactory factory = new ChineseFoodFactory();
        // 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象
        Food food = factory.makeFood("A");
    }
}

使用场景

BeanFactory

3.单例模式

单例非为饿汉式单例模式和饱汉式单例模式,差别在于创建实例的时间。

饿汉式:

public class Singleton {
    // 首先,将 new Singleton() 堵死
    private Singleton() {};
    // 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
    // 瞎写一个静态方法。这里想说的是,如果我们只是要调用 Singleton.getDate(...),
    // 本来是不想要生成 Singleton 实例的,不过没办法,已经生成了
    public static Date getDate(String mode) {return new Date();}
}

DCL饱汉式:

public class Singleton {
    // 首先,也是先堵死 new Singleton() 这条路
    private Singleton() {}
    // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
    private static volatile Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            // 加锁
            synchronized (Singleton.class) {
                // 这一次判断也是必须的,不然会有并发问题
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

嵌套类:该单例创建方式最经典,推荐使用。

public class Singleton3 {

    private Singleton3() {}
    // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
    private static class Holder {
        private static Singleton3 instance = new Singleton3();
    }
    public static Singleton3 getInstance() {
        return Holder.instance;
    }
}

使用场景:Spring下默认的bean均为singleton

4.模板方法模式

模板方法模式就是有一个抽象类,抽象类中包含了一个模板方法和其他的抽象方法或者正常的方法,抽象的方法由继承的类去实现,实现类通过调用模板方法完成了方法的组合调用和自由实现。也就是说,模板方法只负责定义第一步应该要做什么,第二步应该做什么,第三步应该做什么,至于怎么做,由子类来实现。

public abstract class AbstractTemplate {
    // 这就是模板方法
      public void templateMethod(){
        init();
        apply(); // 这个是重点
        end(); // 可以作为钩子方法
    }
    protected void init() {
        System.out.println("init 抽象层已经实现,子类也可以选择覆写");
    }
      // 留给子类实现
    protected abstract void apply();
    protected void end() {
    }
}

实现类:

public class ConcreteTemplate extends AbstractTemplate {
    public void apply() {
        System.out.println("子类实现抽象方法 apply");
    }
      public void end() {
        System.out.println("我们可以把 method3 当做钩子方法来使用,需要的时候覆写就可以了");
    }
}

使用场景:spring中的JdbcTemplate

5.策略模式

策略模式是一种比较常见的设计模式,策略模式中先定义一个策略接口,再定义几个实现了策略接口的类。最后还需要定义一个使用策略的类,如下:

策略接口:

public interface Strategy {
   public void draw(int radius, int x, int y);
}

实现了策略接口的类:

public class RedPen implements Strategy {
   @Override
   public void draw(int radius, int x, int y) {
      System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
   }
}
public class GreenPen implements Strategy {
   @Override
   public void draw(int radius, int x, int y) {
      System.out.println("用绿色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
   }
}
public class BluePen implements Strategy {
   @Override
   public void draw(int radius, int x, int y) {
      System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y);
   }
}

使用策略的类:

public class Context {
   private Strategy strategy;

   public Context(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeDraw(int radius, int x, int y){
      return strategy.draw(radius, x, y);
   }
}

使用示例:

public static void main(String[] args) {
    Context context = new Context(new BluePen()); // 使用绿色笔来画
      context.executeDraw(10, 0, 0);
}

使用场景:spring中在实例化对象的时候用到Strategy模式在SimpleInstantiationStrategy中有如下代码说明了策略模式的使用情况:

6.代理模式

代理模式也是比较常见的一种设计模式,在Spring的AOP源码中有使用。代理模式中,首先需要有一个接口和接口的实现类,代理类同样实现了接口,并且在实现需要被代理的方法前后做了相应的增强,中间是调用需要被代理的对象的方法。

接口:

public interface FoodService {
    Food makeChicken();
    Food makeNoodle();
}

接口的实现类:

public class FoodServiceImpl implements FoodService {
    public Food makeChicken() {
          Food f = new Chicken()
        f.setChicken("1kg");
          f.setSpicy("1g");
          f.setSalt("3g");
        return f;
    }
    public Food makeNoodle() {
        Food f = new Noodle();
        f.setNoodle("500g");
        f.setSalt("5g");
        return f;
    }
}

代理类:同样实现了接口,内部需要注入一个接口实现类的实例对象。

// 代理要表现得“就像是”真实实现类,所以需要实现 FoodService
public class FoodServiceProxy implements FoodService {

    // 内部一定要有一个真实的实现类,当然也可以通过构造方法注入
    private FoodService foodService = new FoodServiceImpl();

    public Food makeChicken() {
        System.out.println("我们马上要开始制作鸡肉了");

        // 如果我们定义这句为核心代码的话,那么,核心代码是真实实现类做的,
        // 代理只是在核心代码前后做些“无足轻重”的事情
        Food food = foodService.makeChicken();

        System.out.println("鸡肉制作完成啦,加点胡椒粉"); // 增强
          food.addCondiment("pepper");

        return food;
    }
    public Food makeNoodle() {
        System.out.println("准备制作拉面~");
        Food food = foodService.makeNoodle();
        System.out.println("制作完成啦")
        return food;
    }
}	

使用示例:

// 这里用代理类来实例化
FoodService foodService = new FoodServiceProxy();
foodService.makeChicken();

1668ac9a05ed0c11tplv-t2oaermark

使用场景:spring的Proxy模式在aop中有体现,比如JdkDynamicAopProxy和Cglib2AopProxy。

posted @ 2021-11-01 17:11  masaike  阅读(70)  评论(0)    收藏  举报