優惠券的工廠與策略模式實現方案

来源:https://www.cnblogs.com/wanglifeng717/archive/2022/06/10/16339222.html
-Advertisement-
Play Games

真正開發中使用最頻繁的模式基本就是【策略】和【工廠】這個兩個模式。 按照"國際慣例"先引入些模式的概念和示例。(示例參考Head First,但是力求比它講的簡潔且清晰) 之後在詳細講解優惠券的設計和模式應用。 所有面向對象入門的時候都是以人、動物為示例。講解什麼是【繼承】等相關概念。這個是符合直覺 ...


 

真正開發中使用最頻繁的模式基本就是【策略】和【工廠】這個兩個模式。

按照"國際慣例"先引入些模式的概念和示例。(示例參考Head First,但是力求比它講的簡潔且清晰)

之後在詳細講解優惠券的設計和模式應用。

 

所有面向對象入門的時候都是以人、動物為示例。講解什麼是【繼承】等相關概念。這個是符合直覺的。

但是在實際應用中,繼承用到的地方有限,它有它的問題,它是一種【強耦合】方式,一般使用【策略模式】【裝飾模式】代替繼承。

 

以鴨子動物設計為例,講解繼承方式存在哪些問題:

 

 

所有鴨子都有quack和swim能力,所以超類實現這兩個功能。

display是抽象方法,每個子類鴨子自己負責實現自己的display功能。

這樣很好的使用了父類繼承能【復用】的特性。

(符合直覺的第一想法,而且還是面向對象學習的不錯的情況)

 

 

有些功能很好界定,有些功能很“尷尬”,例如fly功能。

fly不能加在超類上,因為不是所有鴨子都有fly功能。

如果加在超類上就導致所有的子類都要實現或者繼承這個可能不適用的方法。

而且也不是所有鴨子都會quack(例如木頭玩具鴨子),那些沒有quack的鴨子,同樣要實現或繼承quack。

想利用繼承來達到代碼復用的目的有以下問題:

  1. 同樣的display功能代碼在子類中重覆,代碼沒有【復用】。
  2. 這些子類鴨子的display、fly代碼是寫死的,想運行時候修改很難。
  3. 由於每個display功能分散在不同的子類鴨子中,很難知道全部的行為。
  4. 我們修改了父類會導致牽一發而動全身。所有鴨子都受到了影響。同時我們修改某個相同類型display行為的時候,需要每個鴨子去找該相同代碼進行修改。

 

設計升級:

 

通過介面的形式,讓“某些”(而非全部)鴨子類型可飛可叫。

誰有需要誰就去實現相應的介面。

例如:你可以飛你就實現flyable介面,你不能飛,你就什麼都不做。

 

通過介面的形式解決了部分問題,因為不是所有子類鴨子都具有fly,和quack行為。沒必要繼承或實現自己不適用的功能。

但是代碼無法【復用】的問題還是存在。

我們每個子類中都維護了display,quack功能,可能很多子類的功能都是一致的,沒有復用起來,修改一類相同行為,要每個類去找,逐個修改。

同時這些代碼都散在每個實現類中,不知道全部的行為。

 

 

設計思路與原則:

軟體項目唯一的共性:【需求不斷變化】

我們要做的就是【識別變化】【隔離變化】,每次迭代或者需求變化的時候,修改範圍可控,模塊之間【松耦合】。

主要最好不要動到那些成熟的已經經過測試和生產驗證的代碼,儘量遵循【開閉原則】。

是否進行隔離有個【單一職責】原則判斷,如果兩個模塊修改的原因是不同的,彼此的修改不一定牽涉到對方的修改。那他們應該隔離。

所謂隔離即代表,他們代碼在不同方法中、或在不同類中、或者不同服務模塊中、甚至是不同系統中。

示例中,每個鴨子的fly和quack會隨著鴨子的不同而不同。我們建立兩組類,一組和fly相關,一組和quack相關。

fly類裡面有各種fly的實現方式。例如:用翅膀飛是一個實現類。用火箭飛是另外一個實現類。

這樣對於使用翅膀飛的一類鴨子,我想辦法把相應的fly類給到它,就實現了fly方法的【復用】和【集中管理】

下麵我們要解決的就是如何將這個用翅膀飛的實現類“給到”這個具體的鴨子類。

 

插播一條概念:

【針對介面編程】

什麼是介面?

介面就是約定好的規範、口令、圖紙。

就好比,各個地方的人,都聽得懂“滾”這個語言介面命令,也有相應的實現。 大家雖然各不相同、想法各異、體能差異。

但是聽到你跟他說“滾”,大家都會執行邁腿這個動作,根據人種不同,有的地方人可能邁腿上步揍你,有的地方的人是邁腿跑路。

這種不同人種的不同反應方式,我們稱為【多態】。

雖然語言介面相同,都是一個“滾”的語音輸入。但是具體實現類不同,反應也是不同的。

例如:電腦主板上有很多介面,這些介面是有明文規定,例如電壓、時序、通訊協議、功能等的。

這些就是規範。你按照這個規範走,就能拿到規範定義的結果和返回。

不同的記憶體廠商都有自己的記憶體條。他們的記憶體晶元、板子方案都是不同的,但是他們的插槽是相同的,他們都是實現了記憶體介面規範。

電腦只要按照記憶體介面規範,發出同樣的指令。任何廠商的記憶體條都能進行存儲操作。

以前經常聽說一句話,一流公司定規範,二流公司做產品。

其實規範就是介面,大公司定義實現方案和方案要實現的介面,其他公司根據自己的原材料實現這些介面,這個產品就落地了。

 

所謂【要針對介面編程,不要針對實現編程】

你學習如何讓一個人滾,一定要學習普通話,因為大多數地方的人都能聽懂,只不過反應不同。

如果你針對某個特定的人群學習,那你這個技能就限定在少數人上,例如閩南語只有福建那塊的人能聽懂。

再比如,你這個電腦主板記憶體介面是針對三星獨家的開發的,指令也只有三星認識,其他品牌的記憶體條甚至都插不上去。

這樣的主板誰會買,綁死在三星上,他說漲價你就要掏錢。不然整個電腦都不能運行。

針對介面實現的板子。我可以換同樣介面的國產便宜的記憶體。還是那句“又不是不能用,李姐萬歲”。

 

解釋完概念,我們看編程上如何應用。

我們以一個人的一天活動為例子。

class PersonDayAct{
     DayAct act = new 碼農();
     act.dayAct();
     act.nightAct();                   
}
act.dayAct();
act.nightAct();
我們都用的介面方法,都是使用介面在編程。好處是如果我們想列印富二代的一天。

DayAct act = new 富二代(); 只需要修改這一行代碼即可。
通過多態,我們就能列印富二代的一天活動。
而且這個new操作,我們能通過稍後的工廠模式代替。如果以後要列印其他人的一天活動。
我們只要新建新的實現類即可。不需要改動以前寫好的經過測試的代碼。符合【開閉原則】


講完【面向介面編程】,我們繼續講如何完善鴨子示例。
替代繼承的方式就是【組合】,多用組合,少用繼承。
“有一個”比“是一個”更好,每一個鴨子都有一個FlyBehavior和一個QuackBehavior,好將飛行和咕咕叫委托給他們處理。
鴨子的行為不是繼承來的,而是和“適當”的對象“組合”而來。
組合的好處:

1.將一類行為封裝成類
2.運行時動態改變行為。

 

public abstract class Duck{

    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public Duck(){
    }
    
    public abstract void dispaly();
    
    public void performQuack(){
        quackBehavior.quack();
    }
    
    public void performFly(){
        flyBehavior.fly();
    }
    
    public void swim(){
        System.out.println("all ducks float,even decoys");
    }
}
Duck
public class Bduck extends Duck{

    public Bduck(){
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }

    public void setFlyBehavior( FlyBehavior fb){
        flyBehavior = fb;
    }
    
    public void setQuackBehavior( QuackBehavior qb){
        quackBehavior = qb;
    }
    
    public void display(){
        System.out.println("i am Bduck");
    }
    
}
Bduck
public class Test{

    public static void main(String[] args){
        Duck d = new Bduck();
        d.performFly();
        d.setFlyBehavior(new FlyRocketPowered());
        d.performFly();
    }
}
Test

 

總結:

策略模式:定義演算法族,分別封裝起來,讓他們之間可以互相替換,此模式讓演算法的變化獨立於使用演算法的客戶。

解釋:示例中鴨子的飛行就有不同的策略,有的用翅膀飛,有的用火箭飛。

不同的人對於“滾”這個指令也有自己不同的應對策略,有的是跑,有的是上前揍你。

而這些策略是可以【復用】和【統一管理】的。我們通過【組合】的方式,將策略“放入”到類中,運行時可以更換不同策略。

而不是通過繼承來獲得這個行為。組合比繼承更加靈活,和方便。

 

但是策略模式還留下了一個問題就是,如何“放入”這個策略對象到類中,如果是new對象的形式,這個就和new的那個策略綁定死了。

我們希望的是,在程式運行過程中,通過輸入參數的不同,動態組合不同的實現類。從而實現不同的行為。

例如:我們通過優惠券的類型欄位獲取不同的優惠券實現類。有的是滿減,有的是折扣,但是程式不關心這些類型。

他只要將價格計算委托到不同的策略上計算出最終價格即可。

 

簡單工廠模式:

工廠的職責就是新建產品。

以下單匹薩為例。pizza介面定義了pizza的製作方法。不同種類的pizza負責各自的實現,不同pizza有的烤的時間長,有的切的塊小。

以下是典型的面向介面編程,甚至還有點策略模式的味道。

 


 
Pizza orderPizza(String type){
    
    Pizza pizza;
    
    if(type.equals("cheese")){
        pizza = new CheesePizza();
    }else if(type.equals("greek")){
        pizza = new GreekPizza();
    }else if(type.equals("pepperoni")){
        pizza = new PepperoniPizza();
    }
    
    pizza.prepare();
    pizza.babke();
    pizza.cut();
    pizza.box();
    return pizza;
}

唯一的問題是,如果我pizza的種類有了增刪,我需要修改if-else這塊代碼。這個就違反了【開閉原則】

我們應該將變化的地方【隔離變化】。

 

簡單工廠:

public class PizzaStore{

    SimplePizzaFactory factory;
    
    public PizzaStore(SimplePizzaFactory factory){
        this.factory = factory;
    }
    
    Pizza orderPizza(String type){
        Pizza pizza = factory.createPizza(type);
        
        pizza.prepare();
        pizza.babke();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

 

public class SimplePizzaFactory{
    public Pizza createPizza(String type){
        Pizza pizza;
    
        if(type.equals("cheese")){
            pizza = new CheesePizza();
        }else if(type.equals("greek")){
            pizza = new GreekPizza();
        }else if(type.equals("pepperoni")){
            pizza = new PepperoniPizza();
        }
        
        return pizza;
    }
}


simplePizzaFactory就乾一件事,就是新建比薩。

對於需要單例的我們可以選用單例模式:

1.單例模式的餓漢式[可用]
public class Singleton {
 
    private static Singleton instance=new Singleton();
    private Singleton(){};
    public static Singleton getInstance(){
        return instance;
    }
}
訪問方式

Singleton instance = Singleton.getInstance();

2.單例模式懶漢式雙重校驗鎖[推薦用]
class Singleton{
    private volatile static Singleton instance = null;

    private Singleton() {
         
    }
    public static Singleton getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance new Singleton();
            }
        }
        return instance;
    }
}
訪問方式

Singleton instance = Singleton.getInstance();

3.內部類[推薦用]

public class Singleton{
 
    
    private Singleton() {};
    
    private static class SingletonHolder{
        private static Singleton instance=new Singleton();
    } 
    
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

訪問方式

Singleton instance = Singleton.getInstance();

需要實例化時,調用getInstance方法,才會裝載SingletonHolder類,從而完成Singleton的實例化。

4.枚舉形式
public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }

}
調用方法:

public class Main {

    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }

}

直接通過Singleton.INSTANCE.doSomething()的方式調用即可。方便、簡潔又安全。

懶漢式單例
單例實現模式

工廠封裝的好處:

  1. 可能很多地方都需要新建pizza對象。如果有pizza種類增刪或改變,我們只需要修改simplePizzaFactory這一個地方。【避免多處修改】,
    有時新建對象沒一行代碼那麼簡單,比如連接池這種對象,集中管理很重要。
  2. createPizza方法可以是static的。好處是不需要實例化對象就可以使用,缺點是不能通過繼承來改變創建方法的行為。
  3. 工廠模式讓我們實現了【依賴倒置】,以前雖然已經面向介面編程,但是我們始終要new出具體實現類,一旦new出了具體實現類,
    雖然是面向介面編程,但是相當於和具體實現綁定死了,運行時無法改變的。
    有了工廠,我們高層組建現在只依賴介面或者抽象類,底層實現類也是依賴介面或者抽象類。不依賴具體的實現類。具體實現類可以運行時通過傳參由工廠動態產生。

工廠封裝的缺點:

  1. 如果有pizza種類增刪或改變,雖然只要修改一處,避免了多處修改。但是還是要修改簡單工廠的if-else,還是有違【開閉原則】。

 

為了遵守【開閉原則】,有兩種方式:升級簡單工廠、工廠方法模式。

升級簡單工廠:

工廠也可以是一個介面或者抽象類,我們工廠也可能有很多種實現方式。

我們先實現了一種AStyleSimplePizzaFactory,如果後續需求變更,pizza種類有添加,我們可以在新建一個BStyleSimplePizzaFactory。

你可以認為這是一種分類方式。例如在中國,豆腐腦廠家。南方和北方都是生產豆腐腦,但是一個甜口一個咸口。

 

pizza店可以按照風味分類:

 

 

交通工具也可以通過類型分類:

其實你也可以不按照這個分類。
就是簡單工廠,裡面通過if-else判斷,建造不同風味的pizza也沒問題。
同樣,你也可以把交通工具不按照“海陸空”方式分類。
我就在簡單工廠中,返回不同類型的交通工具實例。完全沒毛病。

but,但是。。。。
按照項目進程,我們不能預測後續要添加多少需求,我們只能按照已知先寫了一個版本,
真的後續添加了產品或者邏輯,我們不修改以前的代碼。我們只能新加工廠和實現類。
就是為了符合【開閉原則】

你可以認為一期只有AStyleSimplePizzaFactory,隨著項目迭代,各種B、C工廠都出來了。

個人以為:
大部分項目開始完全沒有必要使用這麼複雜的簡單工廠,【簡單軟體有簡單軟體的設計】,
後續迭代去修改工廠類,或者有需求之後慢慢演進到這種升級版的簡單工廠才是正途。

老法師都是想著簡潔高效,新手才想著一定要高級有逼格。

public interface Moveable {
    void run();
}

public class Car implements Moveable{
    @Override
    public void run() {
        System.out.println("driving.....");
    }
}

public class Plane implements Moveable{
@Override
    public void run() {
        System.out.println("flying...");
    }
}
//交通工具工廠
public abstract class VehicleFactory {
//具體生成什麼交通工具由子類決定,這裡是抽象的。
    public abstract Moveable create();
}

//Car工廠類
public class CarFactory extends VehicleFactory{
    
    @Override
    public Moveable create() {
        //單例、多例、條件檢查自己控制
        return new Car();
    }
}
//飛機工廠類
public class PlaneFactory extends VehicleFactory {
    
    @Override
    public Moveable create() {
        //單例、多例、條件檢查自己控制
        return new Plane();
    }
}

public class Test{
    public static void main(String[] args){

        VehicleFactory factory = new PlaneFactory();
    Moveable m = factory.create();
    m.run();

    //換成Car工廠
    factory = new CarFactory();
    m = factory.create();
    m.run();

    }    
}

交通工具工廠
交通工具工廠

 

工廠方法模式:

 

 

public abstract class PizzaStore{
    
    public Pizza orderPizza(String type){
        Pizza pizza;
        
        pizza = createPizza(type);
        
        pizza.prepare();
        pziza.bake();
        pizza.cut();
        pizza.box();
        
        return pizza;
    }
    
    abstract Pizza createPizza(String type);
}

public class AStylePizzaStore extends PizzaStore{
     public Pizza createPizza(String type){
        if(type.equals("chesse")){
            pizza = new AStyleChessePizza();
        }else if(type.equals("peperoni")){
            pizza = new AStylePepperoniPizza();
        }
    }
}


調用的時候即:

PizzaStore  store = new AStylePizzaStore();
store.orderPizza("cheese");
 

 

 

工廠方法模式:
定義一個創建對象的介面,但是由子類決定要實例化類時哪一個。
工廠方法讓類把實例化推遲到子類。

 

 工廠方法示例:

 

 

 

 

工廠方法好處:

1.將很多方法和流程固化在父類中,有利於標準化操作,將產品的實現和使用【解耦】。

2.當我們新增產品的時候,或者產品有其他風格和實現時,我們能根據【開閉原則】,新加新的子類即可。

3.工廠方法可以不是抽象的,相當於給了一個預設的實現方式。

工廠方法的缺點:

1.隨著業務增長,可能子類越來越多,難於管理(有抽象工廠管理)。

2.無論是簡單工廠升級版,還是工廠方法。我們很多時候升級不是非黑即白,用新工廠代替舊工廠那麼簡單,或者新工廠就舊工廠各管各的,而是兩個工廠同時存在。

例如:我原來要做甜豆花,現在有要做咸豆花,但是主體業務邏輯不動。如果是新加一個子類。
我們如何動態的指定工廠呢?在搞一個工廠的工廠嗎?突然感覺簡單工廠YYDS了。

其實我們還是要分清,這個新的產品添加,是原來的業務邏輯不動,還是原來的業務邏輯代碼需要變動。

如果原來的主邏輯代碼不動,我們應該需要修改if-else的,因為本質是參數有增加。

如果是拓展的,我們應該是要新建子類,然後拓展新加的代碼使用新加的子類。

 

 

至於什麼時候用介面,什麼時候用抽象類:

假如這個概念在我們腦子是確確實實存在的,就用抽象類。或者你有可復用的方法希望子類繼承直接用。
假如這個概念只是某些方面的特性:比如會飛的,會跑的,就用介面
假如兩個概念模糊的時候,不知道選擇哪個的時候,就用介面,原因是java是單繼承,多介面實現,這個繼承能力很寶貴,從實現了這個介面後,還能從其它的抽象類繼承,更靈活。

 

 抽象工廠:

為了控制工廠子類的數量。不必給每一個產品分配一個工廠類。可以將產品分組,每組中的不同產品有同一個工廠類的不同方法來創建。

 這個和簡單工廠的升級版本很像。但是註意抽象工廠是一個工廠生成不同的東西。是按照系列生產。

我們裝備美式裝備,裡面是含有手槍、大炮等一系列的。

我們裝備德式裝備,裡面又是一套手槍、大炮、汽車等。

 

 

 

//交通工具
public abstract class Vehicle {
    //實現由子類決定
    public abstract void run();
}
//食物
public abstract class Food {
    public abstract void printName();
}
//武器
public abstract class Weapon {
    //
    public abstract void shoot();
}
產品介面
//抽象工廠
public abstract class AbstractFactory {
    //生產 交通工具
    public abstract Vehicle createVehicle();
    //生產 武器
    public abstract Weapon createWeapon();
    //生產食物
    public abstract Food createFood();
}

//哈利波特的魔法工廠
public class MagicFactory extends AbstractFactory {
//交通工具:掃把
    public Vehicle createVehicle(){
        return new Broom();
    }
    
    //武器:魔法棒
    public Weapon createWeapon(){
        return new MagicStick();
    }
    //食物:毒蘑菇
    public Food createFood(){
        return new MushRoom();
    }
}

//預設的工廠
public class DefaultFactory extends AbstractFactory{
@Override
    public Food createFood() {
        return new Apple();
    }
@Override
    public Vehicle createVehicle() {
        return new Car();
    }
@Override
    public Weapon createWeapon() {
        return new AK47();
    }
}
工廠
public class Car extends Vehicle{
    @Override
    public void run() {
        System.out.println("冒著煙奔跑中...");
    }
}
//掃帚
public class Broom extends Vehicle{
@Override
    public void run() {
        System.out.println("掃帚搖著尾巴呼呼呼...");
    }
}
//食物:毒蘑菇
public class MushRoom extends Food {
@Override
    public void printName() {
        System.out.println("mushroom");
    }
}
public class Apple extends Food {
    @Override
    public void printName() {
        System.out.println("apple");
    }
}
public class AK47 extends Weapon{
public void shoot(){
        System.out.println("噠噠噠....");
    }
}
//武器:魔法棒
public class MagicStick extends Weapon {
    @Override
    public void shoot() {
        System.out.println("fire hu hu hu ...");
    }
}
產品
//換一個工廠,只需要改動這一處,就可以了,換一個工廠,就把生產的系列產品都換了
        AbstractFactory factory =  new DefaultFactory(); //new DefaultFactory();
        //換一個工廠
        Vehicle vehicle = factory.createVehicle();
        vehicle.run();
        Weapon weapon = factory.createWeapon();
        weapon.shoot();
        Food food = factory.createFood();
        food.printName();
測試

抽象工廠類圖:

 

 

 

 抽象工廠允許客戶使用抽象介面來創建一組相關的產品,而不需要關心實際產出的具體產品是什麼。

這樣客戶從具體的產品中【解耦】

 抽象工廠的createProductA這種方法看起來很像工廠方法。父類定義,子類實現。

 

總結:

簡單工廠:唯一工廠類,一個產品抽象類,工廠類的創建方法依據入參判斷並創建具體產品對象。

工廠方法:多個工廠類,一個產品抽象類,利用多態創建不同的產品對象,避免了大量的if-else判斷。

抽象工廠:多個工廠類,多個產品抽象類,產品子類分組,同一個工廠實現類創建同組中的不同產品,減少了工廠子類的數量。

 

 

實際應用舉例:

策略和工廠應用的範圍實在太頻繁了,不用特別舉例子。

以優惠券為例。

 

 優惠券分類型:滿減券、折扣券、等等。這些券類型就是決定了算價格的時候如何核銷。這就是一個策略。和不同的鴨子怎麼飛是一樣道理。

同樣優惠券還有適用範圍。到底適用於那些商品、門店、等等。

優惠券有很多投放,這個投放可能在很多渠道和活動是共用的。例如:A券就投放100張,在主頁活動中心、線下掃碼同時領取。領完為止。

 

思路:

優惠券最主要的:優惠方式及計算、有效期方式及計算、適用範圍及計算。

將優惠打折方式作為一種策略。組合到優惠券的屬性中。就如同鴨子組合了一個飛行的策略。

同理優惠券有效期計算,有的是立即生效,有的是固定時間生效等。

優惠券適用範圍目前只有預設方式。

通過簡單參數化工廠:

通過券類型code來獲取不同打折優惠策略實例,

通過券validity_type獲取不同有效期計算的策略實例。

適用範圍,目前只有預設計算方式。無須參數化工廠。

 

 

 

氣氛都哄到這了,就順道講下剩下的兩種創建型模式:原型模式、建造者模式。

原型模式:

 

 

public abstract class Shape implements Cloneable {
   
   private String id;
   protected String type;
   
   abstract void draw();
   
   public String getType(){
      return type;
   }
   
   public String getId() {
      return id;
   }
   
   public void setId(String id) {
      this.id = id;
   }
   
   public Object clone() {
      Object clone = null;
      try {
         // 淺拷貝    
         clone = super.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
      }
      return clone;
   }
}
Shape
public class Rectangle extends Shape {
 
   public Rectangle(){
     type = "Rectangle";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Rectangle::draw() method.");
   }
}

public class Square extends Shape {
 
   public Square(){
     type = "Square";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Square::draw() method.");
   }
}

public class Circle extends Shape {
 
   public Circle(){
     type = "Circle";
   }
 
   @Override
   public void draw() {
      System.out.println("Inside Circle::draw() method.");
   }
}
ConcreteShape
public class ShapeCache {
    
   private static Hashtable<String, Shape> shapeMap 
      = new Hashtable<String, Shape>();
 
   public static Shape getShape(String shapeId) {
      Shape cachedShape = shapeMap.get(shapeId);
      return (Shape) cachedShape.clone();
   }
 
   // 對每種形狀都運行資料庫查詢,並創建該形狀
   // shapeMap.put(shapeKey, shape);
   // 例如,我們要添加三種形狀
   public static void loadCache() {
      Circle circle = new Circle();
      circle.setId("1");
      shapeMap.put(circle.getId(),circle);
 
      Square square = new Square();
      square.setId("2");
      shapeMap.put(square.getId(),square);
 
      Rectangle rectangle = new Rectangle();
      rectangle.setId("3");
      shapeMap.put(rectangle.getId(),rectangle);
   }
}
ShapeCache
public class PrototypePatternDemo {
   public static void main(String[] args) {
      ShapeCache.loadCache();
 
      Shape clonedShape = (Shape) ShapeCache.getShape("1");
      System.out.println("Shape : " + clonedShape.getType());        
 
      Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
      System.out.prin

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 導讀: 大家好,今天主要分享數據分析平臺的平臺演進以及我們在上面沉澱的一些數據分析方法是如何應用的。 具體分以下四部分: Part1:主要介紹下我所在的部門,數據平臺部主要是做什麼的,大概涉及到哪些業務,在整個數據流程當中數據平臺部負責哪些東西; Part2:既然我們講數據分析平臺,那麼數據分析是什 ...
  • 前幾篇我們一起學習了 SQL 如何對錶進行創建、更新和刪除操作、SQL SELECT WHERE 語句如何指定一個或多個查詢條件 和 SQL 如何插入、刪除和更新數據 等資料庫的基本操作方法。 從本文開始,我們將會在這些基本方法的基礎上,學習一些實際應用中的方法。 本文將以此前學過的 SELECT ...
  • 華為運動健康服務(HUAWEI Health Kit)為三方生態應用提供了REST API介面,通過其介面可訪問資料庫,為用戶提供運動健康類數據服務。在實際的集成過程中,開發者們可能會遇到各種問題,這裡我們將典型問題進行分享和總結,希望為其他遇到類似問題的開發者提供參考。 1. 註冊訂閱通知能力後, ...
  • springboot+layui 整合百度富文本編輯器ueditor入門使用教程(踩過的坑) 寫在前面: ​ 富文本編輯器,Multi-function Text Editor, 簡稱 MTE, 是一種可內嵌於瀏覽器,所見即所得的文本編輯器。 ​ UEditor 是由百度「FEX前端研發團隊」開發的 ...
  • 基於Linux環境下的個人網站搭建 一.下載工具 遠程主機: 1.jdk 下載地址 下拉網頁看到Java SE 8u221,選擇伺服器 JRE 選擇linux.tar.gz文件下載 o 2.tomcat 下載地址 選擇.tar.gz文件下載 本地: 1.下載putty:使用Windows遠程連接Li ...
  • 註意:編程式導航(push|replace)才會有這種情況的異常,聲明式導航是沒有這種問題,因為聲明式導航內部已經解決這種問題。 這種異常,對於程式沒有任何影響的。 為什麼會出現這種現象: 由於vue-router最新版本3.5.2,引入了promise,當傳遞參數多次且重覆,會拋出異常,因此出現上 ...
  • 什麼是生命周期? 從出生到成長,最後到死亡,這個過程的時間可以理解為生命周期。 React中的組件也是這麼一個過程。 React的生命周期分為三個階段:掛載期(也叫實例化期)、更新期(也叫存在期)、卸載期(也叫銷毀期)。 在每個周期中React都提供了一些鉤子函數。 生命周期的描述如下: 掛載期:一 ...
  • 1 /* Polygon 多邊形 2 3 parameter: 4 path: Array[x, y]; 5 6 attribute: 7 8 //只讀屬性 9 path: Array[x, y]; 10 11 method: 12 add(x, y): this; //x,y添加至path; 13 ...
一周排行
    -Advertisement-
    Play Games
  • Timer是什麼 Timer 是一種用於創建定期粒度行為的機制。 與標準的 .NET System.Threading.Timer 類相似,Orleans 的 Timer 允許在一段時間後執行特定的操作,或者在特定的時間間隔內重覆執行操作。 它在分散式系統中具有重要作用,特別是在處理需要周期性執行的 ...
  • 前言 相信很多做WPF開發的小伙伴都遇到過表格類的需求,雖然現有的Grid控制項也能實現,但是使用起來的體驗感並不好,比如要實現一個Excel中的表格效果,估計你能想到的第一個方法就是套Border控制項,用這種方法你需要控制每個Border的邊框,並且在一堆Bordr中找到Grid.Row,Grid. ...
  • .NET C#程式啟動閃退,目錄導致的問題 這是第2次踩這個坑了,很小的編程細節,容易忽略,所以寫個博客,分享給大家。 1.第一次坑:是windows 系統把程式運行成服務,找不到配置文件,原因是以服務運行它的工作目錄是在C:\Windows\System32 2.本次坑:WPF桌面程式通過註冊表設 ...
  • 在分散式系統中,數據的持久化是至關重要的一環。 Orleans 7 引入了強大的持久化功能,使得在分散式環境下管理數據變得更加輕鬆和可靠。 本文將介紹什麼是 Orleans 7 的持久化,如何設置它以及相應的代碼示例。 什麼是 Orleans 7 的持久化? Orleans 7 的持久化是指將 Or ...
  • 前言 .NET Feature Management 是一個用於管理應用程式功能的庫,它可以幫助開發人員在應用程式中輕鬆地添加、移除和管理功能。使用 Feature Management,開發人員可以根據不同用戶、環境或其他條件來動態地控制應用程式中的功能。這使得開發人員可以更靈活地管理應用程式的功 ...
  • 在 WPF 應用程式中,拖放操作是實現用戶交互的重要組成部分。通過拖放操作,用戶可以輕鬆地將數據從一個位置移動到另一個位置,或者將控制項從一個容器移動到另一個容器。然而,WPF 中預設的拖放操作可能並不是那麼好用。為瞭解決這個問題,我們可以自定義一個 Panel 來實現更簡單的拖拽操作。 自定義 Pa ...
  • 在實際使用中,由於涉及到不同編程語言之間互相調用,導致C++ 中的OpenCV與C#中的OpenCvSharp 圖像數據在不同編程語言之間難以有效傳遞。在本文中我們將結合OpenCvSharp源碼實現原理,探究兩種數據之間的通信方式。 ...
  • 一、前言 這是一篇搭建許可權管理系統的系列文章。 隨著網路的發展,信息安全對應任何企業來說都越發的重要,而本系列文章將和大家一起一步一步搭建一個全新的許可權管理系統。 說明:由於搭建一個全新的項目過於繁瑣,所有作者將挑選核心代碼和核心思路進行分享。 二、技術選擇 三、開始設計 1、自主搭建vue前端和. ...
  • Csharper中的表達式樹 這節課來瞭解一下表示式樹是什麼? 在C#中,表達式樹是一種數據結構,它可以表示一些代碼塊,如Lambda表達式或查詢表達式。表達式樹使你能夠查看和操作數據,就像你可以查看和操作代碼一樣。它們通常用於創建動態查詢和解析表達式。 一、認識表達式樹 為什麼要這樣說?它和委托有 ...
  • 在使用Django等框架來操作MySQL時,實際上底層還是通過Python來操作的,首先需要安裝一個驅動程式,在Python3中,驅動程式有多種選擇,比如有pymysql以及mysqlclient等。使用pip命令安裝mysqlclient失敗應如何解決? 安裝的python版本說明 機器同時安裝了 ...