【设计模式系列】6.外观模式和策略模式

目录

一、外观模式

1、外观模式特点

2、应用举例

二、策略模式

1、策略模式特点

2、应用举例

3、Java项目中使用策略模式场景


一、外观模式

1、外观模式特点

  • 为系统对外提供一个统一的入口,可以对客户端隐藏子系统内部实现的细节,也降低了客户端与子系统类之间的耦合度;
  • 例如Spring MVC中的 DispaterServlet,所有的Controller都是通过DispaterServlet统一暴露。

2、应用举例

场景一:

比如,一个项目团队有需求人员,开发人员,测试人员等组成,从需求,前后端开发,测试,部署上线的每一步,他们按照各自负责的内容进行产品开发就行。但是项目越来越大,问题越来越多的时候,需要一个技术组长去协调问题,解决遇到的问题,控制项目进度,组长在外观模式中起到了统一整个团队的作用,什么问题都可以通过他来协调解决。

// 开发人员
public class Developer {
    public void develop(){
        System.out.println("有一个开发的问题??");
    }
}
// 测试人员
public class Tester {
    public void test(){
        System.out.println("有一个测试的问题.....");
    }
}

// 技术组长:收集开发与测试的问题,统一解决
public class Leader {
    Developer developer = new Developer();
    Tester tester = new Tester();

    public void processProblem() {
        developer.develop();
        tester.test();
    }
}

场景二:

又比如Java Web项目开发中常常用到Controller、Service、Dao(或者Mapper)这种开发模式,它的本质是一种外观模式。

  • Controller层相当于一个统一的入口,不同的业务处理类XxxService的实例通过注解@Autowired注入到Spring IOC容器中,并进行封装操作。
@RestController
@RequestMapping("/order")
public class OrderController {
    @Autowired
    private OrderService orderService;
    @Autowired
    private WorkSenderService workSenderService;
}
  • 同理Service层也相当于一个统一的入口,不同的处理数据库操作的XxxDao或者XxxMapper的实例通过注解@Autowired注入到Spring IOC容器中,并进行业务处理。

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private GoodsMapper goodsMapper;
}

二、策略模式

1、策略模式特点

  • 针对一组算法,将每一种算法都封装到具有共同接口的独立的类中,从而使它们可以相互替换,算法可以在不影响客户端的情况下发生变化,从而改变不同的功能;
  • 客户端增加行为不用修改原有的代码,只要添加一种策略(或者行为)即可,体现了开闭原则;
  • 策略模式改变了传统的封装继承方式,更多的体现了面向接口编程。

策略模式中有三种角色:

  1. 抽象策略角色:定义的是公共算法的接口。
  2. 具体策略角色:定义的是不同的算法策略的具体内容。
  3. 真实角色:持有公共算法的接口对象的引用,便于客户端使用不同的算法策略。

2、应用举例

比如加减乘除等数学运算,计算是一个共同的算法,而采取加法,减法等方式是不同的策略,如图所示:

  • Demo实现:

/** 定义抽象策略角色(公共算法的部分)*/
public interface CalcStrategy {
    int calc(int i1,int i2);  // 进行int类型的两数之间的运算
}

/**定义具体策略角色(实现不同算法的部分) */
public class AddStrategy implements CalcStrategy {
    @Override
    public int calc(int i1, int i2) {
        return i1 + i2;  // 加法运算策略
    }
}

public class SubStrategy implements CalcStrategy{
    @Override
    public int calc(int i1, int i2) {
        return i1 - i2;  // 减法运算策略
    }
}

public class MulStrategy implements CalcStrategy {
    @Override
    public int calc(int i1, int i2) {
        return i1 * i2;  // 乘法运算策略
    }
}

/** 定义真实角色(持有抽象角色对象的引用,只有运行时才知道运用哪个运算策略,便于实现各自计算策略)*/
public class CalcClass {
    // 通过构造器方式注入CalcStrategy接口对象
    private CalcStrategy calcStrategy;
    public CalcClass(CalcStrategy calcStrategy) {
        this.calcStrategy = calcStrategy;
    }

    public int calculate(int a, int b) {
        return calcStrategy.calc(a, b);
    }
}
  • 测试:

@Test
void testStrategy() {
    CalcClass addCalc = new CalcClass(new AddStrategy());
    logger.info("6+7=" + addCalc.calculate(6, 7));
    CalcClass subCalc = new CalcClass(new SubStrategy());
    logger.info("10-2=" + subCalc.calculate(10, 2));
    CalcClass mulCalc = new CalcClass(new MulStrategy());
    logger.info("10*2=" + mulCalc.calculate(10, 2));
}
// 运行结果:
 6+7=13
 10-2=8
 10*2=20
  • 如果需要增加除法,求余等运算,相应的增加DivStrategy、ModStrategy等运算策略即可,这样的策略设计模式符合开闭原则。

3、Java项目中使用策略模式场景

第一种:与Spring框架结合时,通过修改XML配置文件的配置,选择切换不同的策略。

<bean id="context" class = "com.it.xxwei.service.impl.Context">
    <property name="stg" ref="stgB"/>
</bean>
<bean id="stgA" class = "com.it.xxwei.service.impl.StrategyA"/>
<bean id="stgB" class = "com.it.xxwei.service.impl.StrategyB"/>

第二种:Spring项目中,不同环境快速切换中间件,如Redis,开发环境使用的是单机版配置,而部署到生产环境则会使用集群配置,利用策略模式可以在部署时切换到集群版本。

思路:

  • 定义一个JedisClient接口,将操作Redis的方法都放在接口里;
  • 再分别定义单机类JedisClientPool、集群类JedisClientCluster,实现JedisClient接口并重写接口的操作方法;
  • 而配置文件的话,将单机版Redis连接配置注释掉,保留集群配置即可。

注意:如SpringBoot项目,可以通过配置文件的spring.profiles.active配置快速切换不同的环境(如开发、测试、生产环境等)

第三种:容错恢复机制。有一个日志操作的场景:要求数据库正常连接时,将日志消息记录在数据库的日志表中,当数据库连接异常时,日志先记录在磁盘文件中,等待数据库连接正常时,将日志文件同步到数据库(可以通过定时任务实现)。

public interface LogStrategy {
    public void log(String msg);
}

public class DbLog implements LogStrategy{
    @Override
    public void log(String msg){
        // 日志记录到数据库
    }
}

public class FileLog implements LogStrategy{
    @Override
    public void log(String msg){
        // 日志记录到磁盘文件
    }
}

public class LogContext {
    public void recordLog(String msg) { 
        LogStrategy strategy = null;
        try{
            strategy = new DbLog();
            strategy .log(msg);
        }catch(Exception e){
            // 数据库连接异常时,暂时记录在磁盘文件
            strategy = new FileLog();
            strategy .log(msg);
        }
    }
}

第四种:Java中使用Comparable和Comparator对引用类型对象排序比较的场景。

可以参考我的另一篇博客:https://blog.csdn.net/qq_29119581/article/details/111826702

猜你喜欢

转载自blog.csdn.net/qq_29119581/article/details/114876219