现在有这样一个需求,通过java实现一个榨汁机示例。榨汁机呢,现在支持水果(比如苹果,香蕉),不同水果出汁比例不一样(比如,1kg苹果智能出0.3kg汁)。对于用户(或者讲客户端)来讲,他只关注只要能榨汁即可(不需要关注具体怎么榨汁的,你捣鼓苹果,它榨出苹果汁,你捣鼓香蕉,它榨出香蕉汁)。对于用户来讲,榨汁机有普通版,升级版,豪华版,不同档次榨汁机功能菜单肯定也不一样。
代码地址:源码地址
1、原料定义
我们分析需求,发现既然是榨汁机,那么肯定是榨汁机能够榨的东西,你给它块石头,也炸不出汁。
该抽象类AbstractFruit派生两个子类,Apple和Banana,对于派生的其他水果,只需继承这个抽象类即可。AbstractFruit包括name、color、weight属性
抽象水果类:AbstractFruit
/**
* @description: 抽象水果类
* @Date : 2018/9/20 下午1:24
* @Author : 石冬冬-Seig Heil
*/
public abstract class AbstractFruit {
/**
* 名称
*/
protected String name;
/**
* 颜色
*/
protected String color;
/**
* 重量
*/
protected double weight;
public AbstractFruit() {
}
public AbstractFruit(String name, String color, double weight) {
this.name = name;
this.color = color;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public String toString() {
return "AbstractFruit{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", weight=" + weight +
'}';
}
}
具体水果类:Apple
/**
* @description: 苹果类
* @Date : 2018/9/20 下午1:27
* @Author : 石冬冬-Seig Heil
*/
public class Apple extends AbstractFruit {
public Apple() {
}
public Apple(String name, String color, double weight) {
super(name, color, weight);
}
@Override
public String toString() {
return super.toString();
}
}
具体水果类:Banana
/**
* @description: 香蕉
* @Date : 2018/9/20 下午1:27
* @Author : 石冬冬-Seig Heil
*/
public class Banana extends AbstractFruit {
public Banana() {
}
public Banana(String name, String color, double weight) {
super(name, color, weight);
}
@Override
public String toString() {
return super.toString();
}
}
2、机器定义
既然是榨汁机,肯定是个机器,机器呢,它支持榨汁,你给他原料(水果)它就可以榨汁,输出是啥呢?肯定是果汁,所以这个机器可以榨汁,给它原料,输出果汁。
榨汁接口:Juicing
/**
* @description: 榨汁接口
* @Date : 2018/9/20 下午1:20
* @Author : 石冬冬-Seig Heil
*/
public interface Juicing {
/**
* 榨汁动作
*/
void press();
}
果汁类:Juice
/**
* @description: 果汁
* @Date : 2018/9/20 下午1:54
* @Author : 石冬冬-Seig Heil
*/
public class Juice {
/**
* 名称
*/
private String name;
/**
* 重量
*/
private double weight;
public Juice(String name, double weight) {
this.name = name;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public String toString() {
return "Juice{" +
"name='" + name + '\'' +
", weight=" + weight +
'}';
}
}
抽象榨汁机:AbstractJuicer
/**
* @description: 抽象榨汁机
* @Date : 2018/9/20 下午1:19
* @Author : 石冬冬-Seig Heil
*/
public abstract class AbstractJuicer implements Juicing {
/**
* 水果
*/
protected AbstractFruit fruit;
/**
* 果汁
*/
protected Juice juice;
/**
* 名称
*/
private String materialName;
/**
*
*/
private double materialWeight;
public AbstractJuicer(AbstractFruit fruit) {
this.fruit = fruit;
}
/**
* 初始化
*/
protected void init(){
materialWeight = fruit.getWeight();
materialName = fruit.getName();
}
/**
* 外部调用
*/
public final void execute(){
init();
press();
}
@Override
public void press() {
AbstractJuiceStrategy strategy = StrategyFactory.create(materialName);
ScaleContext scaleContext = new ScaleContext(materialWeight);
strategy.setContext(scaleContext);
strategy.execute();
juice = new Juice(materialName,scaleContext.getOutWeight());
}
/**
* 获取果汁
* @return
*/
public Juice getJuice() {
return juice;
}
}
我们从上述代码中可以分析,上面使用了模板方法模式。对外暴露的方法声明未final类型,是不允许被重写的,内部先初始化原料,然后调用榨汁接口,而实现的榨汁接口,内部先初始化配比上线文,通过调用配比榨汁策略,给出对应的原料名称返回配比结果,根据配比结果,最后输出果汁。
通用榨汁机类:GeneralJuicer
/**
* @description: 通用榨汁机
* @Date : 2018/9/20 下午7:40
* @Author : 石冬冬-Seig Heil
*/
public class GeneralJuicer extends AbstractJuicer {
public GeneralJuicer(AbstractFruit fruit) {
super(fruit);
}
}
3、榨汁比例策略
不同水果榨汁比例肯定不一样,既然达到系统预置,肯定榨汁机根据菜单预置功能从生产就确定的了。
比例配置类:ScaleConfig
这里我们通过一个枚举定义,指定不同水果配置比例,后期扩展时,只需要添加枚举成员即可。
/**
* @description: 比例配置
* @Date : 2018/9/20 下午3:42
* @Author : 石冬冬-Seig Heil
*/
public enum ScaleConfig {
APPLE("苹果",0.6),
BANANA("香蕉",0.3)
;
ScaleConfig(String meterial, double scale) {
this.meterial = meterial;
this.scale = scale;
}
/**
* 原料
*/
private String meterial;
/**
* 原料与榨汁输出比例
*/
private double scale;
public String getMeterial() {
return meterial;
}
public double getScale() {
return scale;
}
public static double getScale(String meterial){
for(ScaleConfig config : ScaleConfig.values()){
if(config.getMeterial().equals(meterial)){
return config.getScale();
}
}
return 0d;
}
}
榨汁策略上线文:ScaleContext
上下文类,包含原料重量和输出重量,即榨汁配比。
/**
* @description: 榨汁策略上线文
* @Date : 2018/9/20 下午3:40
* @Author : 石冬冬-Seig Heil
*/
public class ScaleContext {
/**
* 原料重量
*/
protected double materialWeight;
/**
* 输出
*/
protected double outWeight;
public ScaleContext(double materialWeight) {
this.materialWeight = materialWeight;
}
public double getMaterialWeight() {
return materialWeight;
}
public void setMaterialWeight(double materialWeight) {
this.materialWeight = materialWeight;
}
public double getOutWeight() {
return outWeight;
}
public void setOutWeight(double outWeight) {
this.outWeight = outWeight;
}
}
抽象榨汁输出策略类:AbstractJuiceStrategy
这里我们抽象出一个策略类,不同水果榨汁比例不一样,只需派生该抽象类即可。
抽象类包含定义了一个抽象方法getScale,同时execute方法,把配比方法骨架封装起来,使用模板方法模式。
/**
* @description: 抽象榨汁输出策略类
* @Date : 2018/9/20 下午3:48
* @Author : 石冬冬-Seig Heil
*/
public abstract class AbstractJuiceStrategy {
/**
* 榨汁上下文对象
*/
protected ScaleContext context;
/**
* 原料名称
*/
protected String material;
/**
* 原料重量
*/
protected double materialWeight;
/**
* 输出
*/
protected double outWeight;
/**
* 获取比例
* @return
*/
abstract double getScale();
public AbstractJuiceStrategy() {
}
/**
* 构造函数
* 通过构造函数注入context
* @param context
*/
public AbstractJuiceStrategy(String material,ScaleContext context) {
this.material = material;
this.context = context;
}
/**
* 初始化
*/
protected void init(){
this.materialWeight = context.getMaterialWeight();
System.out.println(MessageFormat.format("原料输入中,当前原料={0},重量={1}",material,materialWeight));
}
/**
* 配比加工
*/
protected void match(){
this.outWeight = this.materialWeight * getScale();
System.out.println("输出重量"+this.outWeight);
}
/**
* 后置处理
*/
protected void after(){
this.context.setOutWeight(outWeight);
}
/**
* 外部调用
*/
public final void execute(){
init();
match();
after();
}
public void setContext(ScaleContext context) {
this.context = context;
}
}
苹果原料果汁榨汁配比策略类:AppleJuiceStrategy
/**
* @description: 苹果原料果汁榨汁配比策略类
* @Date : 2018/9/20 下午3:58
* @Author : 石冬冬-Seig Heil
*/
public class AppleJuiceStrategy extends AbstractJuiceStrategy {
@Override
double getScale() {
return ScaleConfig.getScale(super.material);
}
public AppleJuiceStrategy() {
}
public AppleJuiceStrategy(String material, ScaleContext context) {
super(material, context);
}
public AppleJuiceStrategy(ScaleContext context) {
super("苹果",context);
}
}
香蕉原料果汁榨汁配比策略类:BananaJuiceStrategy
/**
* @description: 香蕉原料果汁榨汁配比策略类
* @Date : 2018/9/20 下午3:58
* @Author : 石冬冬-Seig Heil
*/
public class BananaJuiceStrategy extends AbstractJuiceStrategy {
@Override
double getScale() {
return ScaleConfig.getScale(super.material);
}
public BananaJuiceStrategy() {
}
public BananaJuiceStrategy(String material, ScaleContext context) {
super(material, context);
}
public BananaJuiceStrategy(ScaleContext context) {
super("香蕉",context);
}
}
策略工厂类:StrategyFactory
对于调用发,我们通过简单工厂实现策略类的实例创建,对于输入原料,不关心具体如何配比,只需要传入原料名称即可,内部根据名称获取不同策略类。
/**
* @description: 策略工厂类
* @Date : 2018/9/20 下午4:04
* @Author : 石冬冬-Seig Heil
*/
public enum StrategyFactory {
APPLE("苹果"),
BANANA("香蕉"),
;
StrategyFactory(String material) {
this.material = material;
}
/**
* 原料
*/
private String material;
public String getMaterial() {
return material;
}
/**
* 策略生产
* @param material
* @return
*/
public static AbstractJuiceStrategy create(String material){
switch (material){
case "苹果":
return new AppleJuiceStrategy("苹果",new ScaleContext(0d));
case "香蕉":
return new BananaJuiceStrategy("香蕉",new ScaleContext(0d));
}
return null;
}
}
4、榨汁机集成器
为什么这么定义这个术语呢,既然是榨汁机,肯定是生产商在生产时,都已经把零部件集成安装完毕,对于用户来讲,只需要暴露使用说明书。你不需要明白榨汁机具体怎么榨汁,水果能榨汁多少,你只需要按照说明书上的要求,即可榨汁你放的水果。
执行器接口:Executor
/**
* @description: 执行器接口
* @Date : 2018/9/20 下午7:38
* @Author : 石冬冬-Seig Heil
*/
public interface Executor {
/**
* 执行
*/
void execute();
}
执行器上下文:WrapperContext
该上线文对象,包括几个成员果汁机,原料,以及输出果汁,所以它承载着是一个相当于中介的作用,对于果汁机,原料,以及输出果汁无需调用彼此,只需要委托给上线文对象即可。
/**
* @description: 程序上下文对象,承载果汁机和水果的包装
* @Date : 2018/9/20 下午1:31
* @Author : 石冬冬-Seig Heil
*/
public class WrapperContext {
/**
* 果汁机
*/
protected AbstractJuicer juicer;
/**
* 原料:水果
*/
protected AbstractFruit fruit;
/**
* 果汁
*/
protected Juice juice;
public WrapperContext(AbstractJuicer juicer, AbstractFruit fruit) {
this.juicer = juicer;
this.fruit = fruit;
}
public AbstractJuicer getJuicer() {
return juicer;
}
public void setJuicer(AbstractJuicer juicer) {
this.juicer = juicer;
}
public Juice getJuice() {
return juice;
}
public void setJuice(Juice juice) {
this.juice = juice;
}
public AbstractFruit getFruit() {
return fruit;
}
public void setFruit(AbstractFruit fruit) {
this.fruit = fruit;
}
public WrapperContext(AbstractJuicer juicer, AbstractFruit fruit, Juice juice) {
this.juicer = juicer;
this.fruit = fruit;
this.juice = juice;
}
}
抽象包装执行器:WrapperExecutor
果汁机,原料,以及输出果汁委托给WrapperContext,那具体执行者,就委托给WrapperExecutor,内部承载着对象的调用。
/**
* @description: 抽象包装执行器
* @Date : 2018/9/20 下午1:31
* @Author : 石冬冬-Seig Heil
*/
public abstract class WrapperExecutor implements Executor{
protected WrapperContext context;
public WrapperExecutor(WrapperContext context) {
this.context = context;
}
@Override
public void execute() {
AbstractJuicer juicer = context.getJuicer();
juicer.execute();
context.setJuice(juicer.getJuice());
}
}
通用包装执行器:GeneralExecutor
/**
* @description: 通用包装执行器
* @Date : 2018/9/20 下午7:47
* @Author : 石冬冬-Seig Heil
*/
public class GeneralExecutor extends WrapperExecutor {
public GeneralExecutor(WrapperContext context) {
super(context);
}
}
5、测试
/**
* @description:
* @Date : 2018/9/20 下午7:39
* @Author : 石冬冬-Seig Heil
*/
public class JuicerTest {
@Test
public void apple(){
AbstractFruit apple = new Apple(StrategyFactory.APPLE.getMaterial(),"红色",1000);
GeneralJuicer juicer = new GeneralJuicer(apple);
WrapperContext context = new WrapperContext(juicer,apple);
GeneralExecutor executor = new GeneralExecutor(context);
executor.execute();
System.out.println(context.getJuice());
}
@Test
public void banana(){
AbstractFruit apple = new Banana(StrategyFactory.BANANA.getMaterial(),"红色",1000);
GeneralJuicer juicer = new GeneralJuicer(apple);
WrapperContext context = new WrapperContext(juicer,apple);
GeneralExecutor executor = new GeneralExecutor(context);
executor.execute();
System.out.println(context.getJuice());
}
}
输出结果
原料输入中,当前原料=香蕉,重量=1,000
输出重量300.0
Juice{name=‘香蕉’, weight=300.0}
原料输入中,当前原料=苹果,重量=1,000
输出重量600.0
Juice{name=‘苹果’, weight=600.0}
整体类图关系
下面的是我的公众号二维码图片,欢迎关注。