iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 6
0
自我挑戰組

初探設計模式系列 第 6

[ Day 6 ] 初探設計模式 - 深入工廠、策略與單例模式

前言

今天是鐵人賽碰到的第一個假日(星期六)...

因為算是夜型人的關係,在晚上效率比較高,

但是公司的上班時間對我而言就算是有點早了啊...(8~9點),

滾到12點狠狠補了眠,還是要面對鐵人賽的現實,

放假但是文章不能斷啊~

昨天討論了單例模式,發現了一些事情,在不同語言實現同樣設計模式中,非常有可能遇到個別的特殊問題,但其中需要注意的基礎是一樣的,因此對於語言的熟悉程度,以及所選擇的語言,很容易的會引響到我們在程式撰寫的時候的思考模式。

今天稍微複習一下我們前幾天學習到的三種模式,他們的定義和應用場景

  • 工廠模式

    • 主要的角色有工廠產品
    • 利用一個工廠的開放給想要商品的人
    • 使用者需要透過工廠取得商品。
  • 策略模式

    • 將一系列演算法封裝起來。
    • 使用者可以根據狀況選擇自己需要的演算法
  • 單例模式

    • 一個單一的類別。
    • 確保只有一個實例被創建。
    • 使用者透過開放的方法取得該實例。

題外話:關於UML類別圖。

可能有些人覺得奇怪怎麼都沒有畫出類別圖之類的,

以我本身的經驗來說,在之前也有斷斷續續學習關於設計模式相關的架構、UML圖、定義解釋,甚至也看過實現的code,但是的掌握也沒有非常的好。

後來換了種方式學習,了解定義和運用場景後,再經由寫程式碼的過程更深入的內化,這時候回去看類別圖,會有「啊~原來如此~」的感受。(抽象的東西看了自以為懂了,後來才覺得遠遠不足啊)

另外在學習了設計模式之後,當然會希望能夠用到現有的專案中,會找機會透過各種方式運用,這時候非常有可能會出現過度設計(Over Design)的狀況,在不需要的狀況下使用設計模式,很有可能會花很多時間,實作出一個可以滿足各種狀況的程式碼,甚至用了錯誤的模式在不適合的狀況下(=浪費時間)。

相反的為了求快而不顧程式碼的品質,就會累積下程式債。有時候為了快速開發需求程式債是必要之惡,因為有些需求是有時效性的(在商業上或系統重大bug),如果慢了可能會導致重大損失,這時候勇敢欠債,未來還債也是必要的選擇!(如果你是能夠瞬間設計的大神等級當然可以不用欠債也在時限內開發完成,但相信只有少數人能達到...)

最後還是要寫一些程式碼當成練習

現在飲料店常常特價,但是有各種特價方式,悠遊卡特價、單品特價、買一送一、加五元多一件之類的...,有沒有辦法漂亮實現?

分析一下特價的種類,悠遊卡特價是結帳方式的特價,因此應該是屬於結帳策略的一種,結帳的策略應該有很多種(悠遊卡、現金、各種支付....),所以這邊用策略模式,另外單品特價、買一送一應該是屬於商品屬性之一,可以做設定的特價策略,策略的種類應該也有限,也是策略模式的樣子,那這樣子在結帳和商品可以用同樣的介面實現。

關於特價策略三種,減價、打折、不折價。

//折價策略的介面
public abstract class IDiscountStrategy {

    double discount;

    private IDiscountStrategy(){}

    public IDiscountStrategy(double discount){
        this.discount = discount;
    }

    abstract public double getValue(double value);

}
//減價策略
public class MinusDiscount extends IDiscountStrategy {

    public MinusDiscount(double discount) {
        super(discount);
    }

    @Override
    public double getValue(double value) {
        return value - discount;
    }
}
//打折策略
public class MultiplyStrategy extends IDiscountStrategy {
    public MultiplyStrategy(double discount) {
        super(discount);
    }

    @Override
    public double getValue(double value) {
        return value * discount;
    }
}
//不折價
public class NoneDiscount extends IDiscountStrategy {


    public NoneDiscount(double discount) {
        super(discount);
    }

    @Override
    public double getValue(double value) {
        return value;
    }
}

關於飲料的商品類

//飲料介面
public abstract class Drink {

    IDiscountStrategy discountStrategy = new NoneDiscount(1);

    double value;

    private Drink(){}

    public Drink(double value) {
        this.value = value;
    }

    public Drink(double value , IDiscountStrategy discountStrategy) {
        this.value = value;
        this.discountStrategy = discountStrategy;
    }

    public double getValue() {
        return discountStrategy.getValue(value);
    }

}
//奶茶
public class MilkTea extends Drink {
    public MilkTea(double value) {
        super(value);
    }

    public MilkTea(double value, IDiscountStrategy discountStrategy) {
        super(value, discountStrategy);
    }

    @Override
    public double getValue() {
        return super.getValue();
    }
}
//咖啡
public class Coffee extends Drink {
    public Coffee(double value) {
        super(value);
    }

    public Coffee(double value, IDiscountStrategy discountStrategy) {
        super(value, discountStrategy);
    }

    @Override
    public double getValue() {
        return super.getValue();
    }
}

實現一下飲料的訂單

public class DrinkOrder {

    //放飲料的list
    private List<Drink> drinkList = new ArrayList<Drink>();

    //加入飲料
    public void addDrink(Drink drink){
        drinkList.add(drink);
    }
	//移除飲料
    public void removeDrink(Drink drink){
        drinkList.remove(drink);
    }

    //飲料總價錢(私有方法)
    private double totalPrice(){

        double totalPrice = 0 ;

        for(Drink d:drinkList){
            totalPrice += d.getValue();
        }

        return totalPrice;

    }

    //訂單總價錢
    public double getTotalPrice(IDiscountStrategy discountStrategy){

        double totalPrice = totalPrice() ;

        return discountStrategy.getValue(totalPrice);

    }

}

稍微測試一下跟預計的一不一樣

public class Test {
    @org.junit.jupiter.api.Test
    public void test(){

        DrinkOrder drinkOrder = new DrinkOrder();
        drinkOrder.addDrink(new Coffee(165));
        drinkOrder.addDrink(new MilkTea(55,new MinusDiscount(20)));
        double price = drinkOrder.getTotalPrice(new NoneDiscount(1));
        //200
        System.out.println(price);
        price = drinkOrder.getTotalPrice(new MultiplyStrategy(0.9));
        //打九折
        System.out.println(price);

    }
}

今天就分享到這邊,稍微複習一下前幾天學到的東西,說了一些自己的體悟(或許有人會覺得我很囉唆),在天瓏買了些書明天想學一些其他的模式,如果想分享的地方或是有什麼意見,歡迎留言或私訊我~


上一篇
[ Day 5 ] 初探設計模式 - 單例模式 (Singleton)
下一篇
[ Day 7 ] 初探設計模式 - 裝飾模式(Decorator)
系列文
初探設計模式30

尚未有邦友留言

立即登入留言