Java设计模式之策略模式(Strategy)

本文深入探讨了策略模式的设计理念,解释了其如何通过封装变化、松耦合和接口编程,实现程序的易维护、易扩展、易服用和高灵活性。通过商场收费系统的案例,详细展示了策略模式的实现过程,包括抽象策略角色、具体策略角色和环境角色的定义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么要学习设计模式?

我们之所以要学习设计模式,并不是把具体的某些模式原原本本地照搬来用,而是通过学习这些模式,了解到底什么是“封装变化”,“对象间松耦合”,“针对接口编程”等概念,从而设计出符合前面所讲到四个特性——易维护,易扩展,易服用,灵活性好的程序。

1.简介

指对象的某个行为不同时间/场景下有不同的实现方式,即实现的算法。

策略模式是定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作(计算实际应收钱),只是实现的方式有所不同(即算法不同),它可以用相同的方式调用所有方法,减少各类算法类和使用算法类之间的耦合。

  1. 特点:定义一组的算法(业务的实现规则);封装具体的每个算法(封装变化);这一族的算法可以互相替代;
  2. 组成抽象的策略角色:策略类,接口或者抽象类;具体的策略角色:包装了相关的算法和行为;环境角色:持有策略类的引用,供客户端调用;
  3. 优点:将公共代码转移到父类中,避免了代码的重复;动态替换继承关系,使得动态替换算法可实现;便于单元测试;
  4. 缺点:使用者必须知道每个具体的策略角色所使用的算法;很多策略类的具体实现,代码臃肿;

UML类图结构为:(引自百度百科)
在这里插入图片描述

2.使用场景

  • 多个类只区别在行为方式不同
  • 需要在不同情况下使用不同策略(算法)来实现,将来可能用其他的方法来实现
  • 对客户端隐藏具体实现,完全隔离开

比如:商场收费系统,旅游的出行方式的选择等都可以使用策略模式来实现。

3.案例(商场收费系统)

具体功能:输入单价以及数量,按照正常情况/打折情况/满返情况计算实收费用。

分析:完成的共同方法都是计算最后实际收款额,不同的情况看成不同的业务规则(算法);

抽象的策略角色:提供抽象计算方法(业务规则)

package com.example.administrator.strategy.strategymode;

/**
 * 创建时间: 2019/1/25 13:36
 * 描述:    抽象的策略角色:可以是接口可以是抽象类
 *          策略模式中的策略类 Strategy
 *          定义所有支持算法的公共接口
 */
@SuppressWarnings("unused")
public abstract class CashSuper {
    /**
     * @param money 收取现金
     * @return 返回应收金额
     */
    public abstract double acceptCash(String money);
}

具体的策略角色:不同情况的计算方式封装成类

正常情况:

package com.example.administrator.strategy.strategymode;

import java.math.BigDecimal;

/**
 * 创建时间: 2019/1/25 13:38
 * 描述:    具体的某个优惠方式,即算法的具体实现
 *          具体的策略角色
 */
@SuppressWarnings("unused")
public class CashNormal extends CashSuper {
    /**
     * @param money 收取现金
     * @return 返回最终的金额
     */
    @Override
    public double acceptCash(String money) {
        BigDecimal value = new BigDecimal(money);
        return value.doubleValue();
    }
}

打折情况:

package com.example.administrator.strategy.strategymode;

import java.math.BigDecimal;

/**
 * 创建时间: 2019/1/25 13:40
 * 描述:    打折算法的具体实现类
 *          具体的策略角色
 */
@SuppressWarnings("unused")
public class CashRebate extends CashSuper {
    private double rebate;

    /**
     * @param rebateRate 打折的程度
     */
    public CashRebate(double rebateRate) {
        this.rebate = rebateRate;
    }

    /**
     * @param money 收取现金
     * @return 返回打折后应收
     */
    @Override
    public double acceptCash(String money) {
        BigDecimal value = new BigDecimal(money);
        return value.multiply(new BigDecimal(String.valueOf(rebate))).doubleValue();

    }
}

满返情况:

package com.example.administrator.strategy.strategymode;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 创建时间: 2019/1/25 13:44
 * 描述:    满返回的具体算法实现
 *          具体的策略角色
 */
@SuppressWarnings("unused")
public class CashReturn extends CashSuper {
    private double moneyCondition;
    private double moneyReturn;

    /**
     * 构造
     *
     * @param moneyCondition 满多少金额
     * @param moneyReturn    返多少金额
     */
    public CashReturn(double moneyCondition, double moneyReturn) {
        this.moneyCondition = moneyCondition;
        this.moneyReturn = moneyReturn;
    }

    @Override
    public double acceptCash(String money) {
        BigDecimal value = new BigDecimal(money);
        BigDecimal bgCondition = new BigDecimal(String.valueOf(this.moneyCondition));
        //判断金额是否大于300
        if (value.compareTo(bgCondition) >= 0) {
            return value.subtract(value.divide(bgCondition, 2, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal(String.valueOf(this.moneyReturn)))).doubleValue();
        } else {
            return value.doubleValue();
        }


    }
}

环境角色:持有策略类的引用,供客户端使用

package com.example.administrator.strategy.strategymode;

import com.example.administrator.strategy.R;

import java.math.BigDecimal;

/**
 * 创建时间: 2019/1/25 13:51
 * 描述: 环境角色:供客户端调用,内部持有策略类的引用
 *       Context层: 添加简单的工厂模式
 *       提供策略的实现
 */
@SuppressWarnings("unused")
public class CashContext {

    private CashSuper cashSuper;

    /**
     * 根据传入的resID创建对应的收费策略
     *
     * @param redId 选择ID
     */
    public CashContext(int redId) {
        switch (redId) {
            //正常
            case R.id.rb_normal:
                cashSuper = new CashNormal();
                break;
            //8折
            case R.id.rb_20_off:
                cashSuper = new CashRebate(0.8d);
                break;
            //6折
            case R.id.rb_40_off:
                cashSuper = new CashRebate(0.6d);
                break;
            //满300-100
            case R.id.rb_over_300_minus_100:
                cashSuper = new CashReturn(300d, 100d);
                break;
        }
    }

    /**
     * 暴露方法
     *
     * @param price 单价
     * @param count 数量
     * @return 实际收的钱
     */
    public double getResult(String price, String count) {
        BigDecimal p = new BigDecimal(price);
        BigDecimal c = new BigDecimal(count);
        return cashSuper.acceptCash(p.multiply(c).toString());
    }
}

客户端使用: 持有环境角色的引用,根据优惠方式进行调用。

package com.example.administrator.strategy;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.example.administrator.strategy.strategymode.CashContext;


public class MainActivity extends AppCompatActivity {

    private Button btnCaculate;
    private EditText etPrice;
    private EditText etCount;
    private TextView tvResult;
    private RadioGroup rgGroup;
    private int resId = -1;
    private CashContext cashContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rgGroup = findViewById(R.id.rg);
        btnCaculate = findViewById(R.id.btn_cac);
        etPrice = findViewById(R.id.et_price);
        etCount = findViewById(R.id.et_count);
        tvResult = findViewById(R.id.tv_result);
        resId = R.id.rb_normal;
        rgGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                resId = checkedId;
            }
        });
        btnCaculate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String price = etPrice.getText().toString();
                String count = etCount.getText().toString();
                if (!TextUtils.isEmpty(price) && !TextUtils.isEmpty(count)) {
                    if ((!price.contains(".") && price.substring(0, 1).equals("0")) ||
                            (!count.contains(".") && count.substring(0, 1).equals("0")))
                        Toast.makeText(MainActivity.this, "输入单价或者数量有问题", Toast.LENGTH_SHORT).show();
                    else {
                        cashContext = new CashContext(resId);
                        tvResult.setText("应收:"+String.valueOf(cashContext.getResult(price, count)));
                    }
                }
            }
        });
    }
}

**

参考自:《大话设计模式》

**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值