设计模式之(委派模式&策略模式)

目录

1.课程学习目标

1.掌握委派模式,精简程序逻辑,提升代码的可读性。

2.通过学习策略模式来消除程序中大量的if..else.. 和switch语句。

3.深刻理解策略模式的应用场景,提高算法的保密性和安全性。

2.内容定位

1.希望通过对于委派模式的学习,让自己写出更加优雅代码的人群。

2.希望通过策略模式的学习,来消除程序中大量的冗余代码和多重条件转移语句的人群。

3.委派模式详解

3.1委派模式的定义

1.委派模式(Delegate Pattern) 的基本作用就是负责任务的调度和分配,和代理模式很像,可以看做是一种特殊情况下的静态代理的全权代理,但是代理模式注重过程,而委派模式注重结果。

2.不属于GOF 23种设计模式之一。

3.属于行为型模式。

4.Delegate 结尾的一般都是委派模式,Dispatcher

3.2 demo案例

3.2.1模拟Boss指派任务给Leader 由员工完成任务执行

main

/**
 * @PackageName: com.raven.pattern.delegate.simple
 * @ClassName: SimpleTest
 * @Blame: raven
 * @Date: 2021-08-01 14:07
 * @Description: 委派模式简单案例 模仿BOSS下达指令给leader BOSS不关心到底具体由谁再干活 只需要让leader完成任务即可
 * leader不直接自己干活,而根据任务的特点 ,调用不同的employee 进行执行任务
 */
public class SimpleTest {

    public static void main(String[] args) {
        Boss boss = new Boss();
        boss.execute("JAVA",new Leader());
        boss.execute("GO",new Leader());

    }
}

定义boss类 leader类

public class Boss {

    public void execute(String command , Leader leader){
        leader.doCommand(command);
    }
}


/**
 * @PackageName: com.raven.pattern.delegate.simple
 * @ClassName: Leader
 * @Blame: raven
 * @Date: 2021-08-01 10:42
 * @Description: leader知道每一个员工所擅长的语言,当有需求到达时,根据需求要求的语言,交于不同的员工进行开发任务。
 */
public class Leader {

    private static Map<String, IEmployee> employeeMap = new HashMap<>();

    private static String JAVA = "JAVA";
    private static String GO = "GO";
    private static String DEFAULT = JAVA;

    static {
        employeeMap.put(JAVA, new EmployeeA());
        employeeMap.put(GO, new EmployeeB());
    }

    public void doCommand(String command) {
        if (!employeeMap.containsKey(command)) {
            employeeMap.get(DEFAULT).doing(command);
        } else {
            employeeMap.get(command).doing(command);
        }
    }
}

定义员工类

/**
 * @PackageName: com.raven.pattern.delegate.simple
 * @ClassName: IEmplyee
 * @Blame: raven
 * @Date: 2021-08-01 10:44
 * @Description: 定义员工接口 规范员工行为
 */
public interface IEmployee {

    /**
     * 员工可以干活
     * @param command 命令行为
     */
    void doing(String command);
}

/**
 * @PackageName: com.raven.pattern.delegate.simple
 * @ClassName: EmployeeA
 * @Blame: raven
 * @Date: 2021-08-01 10:39
 * @Description:
 */
public class EmployeeA implements IEmployee{
    @Override
    public void doing(String command) {
        System.out.println("我是员工,A我擅长JAVA,我执行" + command + "命令");
    }
}

/**
 * @PackageName: com.raven.pattern.delegate.simple
 * @ClassName: EmployeeB
 * @Blame: raven
 * @Date: 2021-08-01 10:39
 * @Description:
 */
public class EmployeeB  implements  IEmployee{

    @Override
    public void doing(String command){
        System.out.println("我是员工B,我擅长GO,我执行"  + command + "命令");
    }
}

3.2.2 模拟spring mvc dispatchServlet 根据不同的uri 执行不同的controller代码

disptacherServlet

/**
 * @PackageName: com.raven.pattern.delegate.mvc
 * @ClassName: DispatcherServlet
 * @Blame: raven
 * @Date: 2021-08-01 14:22
 * @Description: 模拟手写mvc dispatcherServlet 委派设计模式
 * <p>
 * 模拟 根据不同的uri 调用不同的controller方法
 */
public class DispatcherServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 完成调度
        doDispatch(req, resp);
    }

    /**
     * 委派模式 将服务调用的事情委派给dispatch
     * 通过dispatch 完成调度,根据请求不同的uri 调用不同controller代码 执行业务逻辑
     *
     * @param req
     * @param resp
     * @throws IOException
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String requestURI = req.getRequestURI();
        String mid = req.getParameter("mid");

        if (StringUtils.equals("getOrderById", requestURI)) {
            new OrderController().getOrderById(mid);
        } else if (StringUtils.equals("getUserById", requestURI)) {
            new UserController().getUserById(mid);
        } else if (StringUtils.equals("logout", requestURI)) {
            new SystemController().logout();
        } else {
            resp.getWriter().write("404 not found !");
        }
    }
}

controller

public class OrderController {
    public void getOrderById(String id){
        System.out.println("获取订单详情!");
    }
}

public class UserController {
    public void getUserById(String mid){
        System.out.println("获取用户信息!");
    }
}

public class SystemController {
    public void logout(){
        System.out.println("退出服务器!");
    }
}

3.3 spring中的委派模式

BeanDefinitionParserDelegate

4.策略模式详解

4.1策略模式的定义

1.策略模式(Stratege pattern) 是指定义了算法家族、分别封装起来,让他们之间可以互相转换,此模式让算法的变化不会影响到使用算法的用户。

2.可以避免多重分支的if..else.. 和switch语句。

4.2策略模式的适用场景

1.加入系统中有很多类,而他们的区别仅仅在于他们的行为不同

2.一个系统需要动态地在几种算法中选择一种。

4.3策略模式优点

1.策略模式符合开闭原则。

2.避免使用多重条件转移语句,如if .. else..语句、switch语句。

3.使用策略模式可以提高算法的保密性和安全性。

4.4策略模式的缺点

1.客户端必须知道所有的策略,并且自行决定使用哪一个策略类。

2.代码中会产生非常多的策略类,增加维护难度。

4.5策略模式案例Demo

4.5.1模拟选购商品不同活动采用不同的优惠策略

活动main

/**
 * @PackageName: com.raven.pattern.strategy.promotion
 * @ClassName: PromotionActivityTest
 * @Blame: raven
 * @Date: 2021-08-01 16:14
 * @Description: 创建促销活动 指定活动名称 根据不同的活动名称返回不同的促销活动内容
 */
public class PromotionActivityTest {
    public static void main(String[] args) {
        /**
         *  避免了if else 判断不同活动使用不同策略 促销活动工厂内部决定返回促销策略
         */
        PromotionActivity promotionActivity618 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategyByPromotionKey(""));
        promotionActivity618.execute();

        PromotionActivity promotionActivity1111 = new PromotionActivity(PromotionStrategyFactory.getPromotionStrategyByPromotionKey("返现活动"));
        promotionActivity1111.execute();
    }
}

/**
 * @PackageName: com.raven.pattern.strategy.promotion
 * @ClassName: PromotionActivity
 * @Blame: raven
 * @Date: 2021-08-01 15:54
 * @Description: 定义促销活动,每一场促销活动都需要指定促销策略
 */
public class PromotionActivity {

    private IPromotionStrategy promotionStrategy;

    public PromotionActivity(IPromotionStrategy promotionStrategy) {
        this.promotionStrategy = promotionStrategy;
    }

    public void execute() {
        promotionStrategy.doPromotion();
    }
}

策略工厂

/**
 * @PackageName: com.raven.pattern.strategy.promotion
 * @ClassName: IPromotionStratrgy
 * @Blame: raven
 * @Date: 2021-08-01 15:50
 * @Description: 定义营销活动策略规范
 */
public interface IPromotionStrategy {
    /**
     * 执行促销活动
     */
    void doPromotion();
}

public class GroupbuyStrategy implements IPromotionStrategy {
    @Override
    public void doPromotion() {
        System.out.println("促销活动为团购活动,五人一起团购,每人减400元!");
    }
}

public class EmptyStrategy implements IPromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("现在没有活动!");
    }
}

public class CashbackStrategy implements IPromotionStrategy{
    @Override
    public void doPromotion() {
        System.out.println("促销活动为返现活动,返现200元!");
    }
}

/**
 * @PackageName: com.raven.pattern.strategy.promotion
 * @ClassName: PromotionStrategyFactory
 * @Blame: raven
 * @Date: 2021-08-01 15:56
 * @Description: 促销活动工厂 懒汉式单例模式 + 策略模式
 */
public class PromotionStrategyFactory {
    private PromotionStrategyFactory() {
    }

    private static PromotionStrategyFactory factory;
    private static Map<String, IPromotionStrategy> PROMOTION_STRATEGY_MAP = new HashMap<>();

    static {
        factory = new PromotionStrategyFactory();
        PROMOTION_STRATEGY_MAP.put(PromotionKeyEnum.CASH_BACK.getPromotionName(), new CashbackStrategy());
        PROMOTION_STRATEGY_MAP.put(PromotionKeyEnum.GROUP_BUY.getPromotionName(), new GroupbuyStrategy());
    }

    public static PromotionStrategyFactory getFactory() {
        return factory;
    }

    /**
     * 根据促销活动名称返回促销活动内容
     * @param promotionName
     * @return
     */
    public static IPromotionStrategy getPromotionStrategyByPromotionKey(String promotionName) {
        IPromotionStrategy promotionStrategy = PROMOTION_STRATEGY_MAP.get(promotionName);
        return Objects.isNull(promotionStrategy) ? new EmptyStrategy() : promotionStrategy;
    }
}

public enum PromotionKeyEnum {
    EMPTY("无活动"),
    GROUP_BUY("团购活动"),
    CASH_BACK("返现活动")
    ;
    @Getter
    private String promotionName;

    PromotionKeyEnum(String promotionName) {
        this.promotionName = promotionName;
    }
}

4.5.2根据不同的支付方式进行不同的支付策略

支付main

/**
 * @PackageName: com.raven.pattern.strategy.pay
 * @ClassName: OrderPayTest
 * @Blame: raven
 * @Date: 2021-08-01 17:05
 * @Description: 模拟用户使用不同支付方式支付订单
 * 根据用户选择的支付方式不同,调用不同的支付体系完成支付操作
 */
public class OrderPayTest {

    public static void main(String[] args) {
        Order order = new Order("0001", "OR00001", 90);
        MsgResult msgResult = order.pay(PayStrategy.WE_CHAT_PAY);
        System.out.println(msgResult);

        System.out.println("====================");
        Order order2 = new Order("0001", "OR00002", 90);
        MsgResult msgResult2 = order2.pay(PayStrategy.ALI_PAY);
        System.out.println(msgResult2);

    }
}

/**
 * @PackageName: com.raven.pattern.strategy.pay
 * @ClassName: Order
 * @Blame: raven
 * @Date: 2021-08-01 16:43
 * @Description: 订单对象 支付时调用支付接口
 */
public class Order {
    private String uid;
    private String orderId;
    private double amount;

    public Order(String uid, String orderId, double amount) {
        this.uid = uid;
        this.orderId = orderId;
        this.amount = amount;
    }

    /**
     * 订单支付 不同支付方式返回不同支付策略
     * @param payType
     * @return
     */
    public MsgResult pay(String payType) {
        Payment payment = PayStrategy.getPayment(payType);
        System.out.println("欢迎使用" + payment.getName() + "支付");
        System.out.println("支付订单号为:" + orderId);
        System.out.println("本次交易金额为" + amount + "元");
        return payment.pay(uid, amount);
    }
}

/**
 * @PackageName: com.raven.pattern.strategy.pay
 * @ClassName: MsgResult
 * @Blame: raven
 * @Date: 2021-08-01 16:45
 * @Description: 支付结果封装
 */
public class MsgResult {
    private int code;
    private String msg;
    private Object data;

    public MsgResult(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    @Override
    public String toString() {
        return "支付状态:" + "[" + code + "], " + msg + ", 交易详情:" + data;
    }
}

支付策略

/**
 * @PackageName: com.raven.pattern.strategy.pay.payport
 * @ClassName: PayStrategy
 * @Blame: raven
 * @Date: 2021-08-01 16:57
 * @Description: 支付策略类 根据不同的支付方式封装返回不同的支付策略
 */
public class PayStrategy {
    private PayStrategy() {
    }

    public static final String ALI_PAY = "支付宝支付";
    public static final String WE_CHAT_PAY = "微信支付";

    private static Map<String, Payment> PAYMENT_MAP = new HashMap<>();

    static {
        PAYMENT_MAP.put(ALI_PAY, new AliPay());
        PAYMENT_MAP.put(WE_CHAT_PAY, new WechatPay());
    }

    public static Payment getPayment(String payType) {
        return PAYMENT_MAP.getOrDefault(payType, new AliPay());
    }
}

public class AliPay extends Payment {
    @Override
    public String getName() {
        return "支付宝支付";
    }

    @Override
    public double queryBalance(String uid) {
        return 100;
    }
}

public class WechatPay  extends Payment {
    @Override
    public String getName() {
        return "微信支付";
    }

    @Override
    public double queryBalance(String uid) {
        return 10;
    }
}

public abstract class Payment {

    /**
     * 获取支付方式名称
     * @return
     */
    public abstract String getName();

    /**
     * 查询账户余额
     * @param uid
     * @return
     */
    public abstract double queryBalance(String uid);

    /**
     * 支付校验 当账号余额不足时,无法完成支付
     * @param uid
     * @param amount
     * @return
     */
    public MsgResult pay(String uid, double amount) {
        if (queryBalance(uid) < amount) {
            return new MsgResult(500, "支付失败", "余额不足!");
        }
        return new MsgResult(200, "支付成功", "支付金额:" + amount);
    }
}

4.6 基于策略模式重写 DispatcherServlet


/**
 * @PackageName: com.raven.pattern.delegate.mvc
 * @ClassName: DispatcherServlet
 * @Blame: raven
 * @Date: 2021-08-01 14:22
 * @Description: 模拟手写mvc dispatcherServlet 委派设计模式
 * <p>
 * 模拟 根据不同的uri 调用不同的controller方法
 */
public class DispatcherServlet extends HttpServlet {

    /**
     * 通过工厂模式将controller封装到List集合中
     */
    private List<Handler> handlerMapping = Lists.newArrayList();

    /**
     * 初始化mapping
     * 通过反射将所有的实体属性封装到List中
     *
     * @throws ServletException
     */
    @Override
    public void init() throws ServletException {
        Class<OrderController> orderClazz = OrderController.class;
        try {
            handlerMapping.add(new Handler(orderClazz.newInstance(),
                    orderClazz.getMethod("getOrderById", String.class),
                    "getOrderById"
            ));
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 完成调度
//        doDispatch(req, resp);

        doDispatch2(req, resp);
    }

    /**
     * 通过反射执行根据不同的url 执行不同的controller中的策略
     * @param req
     * @param resp
     */
    private void doDispatch2(HttpServletRequest req, HttpServletResponse resp) {
        String requestURI = req.getRequestURI();
        String mid = req.getParameter("mid");

        Handler handler = null;
        for (Handler h : handlerMapping) {
            if (StringUtils.equals(requestURI, h.getUrl())) {
                handler = h;
                break;
            }
        }

        try {
            Object o = handler.getMethod().invoke(handler.getController(), mid);
            resp.getWriter().write(o.toString());
        } catch (IllegalAccessException | InvocationTargetException | IOException e) {
            e.printStackTrace();
        }
    }

    @Data
    class Handler {
        private Object controller;
        private Method method;
        private String url;

        public Handler(Object controller, Method method, String url) {
            this.controller = controller;
            this.method = method;
            this.url = url;
        }
    }
}

4.7spring中的策略模式

InstantiationStrategyResource

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦