文章目錄
現在有這樣一個需求,通過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}
整體類圖關係
下面的是我的公衆號二維碼圖片,歡迎關注。