headfirst設計模式(4)—工廠模式

来源:https://www.cnblogs.com/skyseavae/archive/2018/01/02/8030304.html
-Advertisement-
Play Games

開篇 天天逛博客園,就是狠不下心來寫篇博客,忙是一方面,但是說忙能有多忙呢,都有時間逛博客園,寫篇博客的時間都沒有?(這還真不好說) 每次想到寫一篇新的設計模式,我總會問自己: 1,自己理解了嗎? 2,我能以一種簡單且有邏輯的方式把它說出來嗎? 不說做到有的放矢,但是一本正經的胡說八道還是要有吧,起 ...


開篇

天天逛博客園,就是狠不下心來寫篇博客,忙是一方面,但是說忙能有多忙呢,都有時間逛博客園,寫篇博客的時間都沒有?(這還真不好說)

每次想到寫一篇新的設計模式,我總會問自己:

1,自己理解了嗎?

2,我能以一種簡單且有邏輯的方式把它說出來嗎?

不說做到有的放矢,但是一本正經的胡說八道還是要有吧,起碼要忽悠得頭頭是道嘛(手動斜眼笑)

關於工廠模式的幾個問題

1,這個是拿來乾什麼的?

2,怎麼用?

3,不用行不行?

第一個和第三個問題,我現在就可以告訴你答案:早點下班,可以

所有的設計模式對我來說都是為了減少工作量。關於減少工作量我的理解是:每個需求,都應該在它適當的時候出現適當的代碼!這個太重要了

代碼偷懶,後期返工多

過度設計,後期返工多

設計模式+經驗可以解決這個問題,其他的我還不知道。沒有經驗怎麼辦?兩個要點:

1,能用

2,簡潔

首先要達到能用,然後就是儘量簡潔,這樣代碼就不會太差。首先你要自己看得懂,然後是讓隊友看得懂。

你知道你隊友看到一堆爛的看都看不懂,也一句註釋都沒有的代碼的時候的心理陰影面積嗎?

這其實也沒什麼,誰沒填過別人的坑呢?關鍵是他知道你家在哪裡,而且還知道你經常走夜路,就問你怕不怕?(卧槽,又跑題了。。)

 

需求:你有一個披薩店,只賣一種披薩,代碼如下:

披薩:

import java.util.ArrayList;
import java.util.List;

/**
 * 披薩類
 * @author skysea
 */
public class Pizza {
    
    private String name;//披薩名稱
    
    private String dough;//麵團
    
    private String sauce;//醬料
    
    private List<String> toppings = new ArrayList<>();//佐料
    
    public Pizza() {
            this.name = "原味披薩";
            this.dough = "原味麵團";
            this.sauce = "原味醬料";
    }
    
    void prepare() {
        System.out.println("開始準備披薩:" + name);
        
        System.out.println("開始處理麵團:" + dough);
        
        System.out.println("添加醬料:" + sauce);
        
        System.out.println("添加佐料:");
        if(toppings.size() > 0) {
            for(String t : toppings) {
                System.out.println(" " + t);
            }
        }
    }
    
    void bake() {
        System.out.println("烘焙25分鐘..");
    }
    
    void cut() {
        System.out.println("披薩切片..");
    }
    
    void box() {
        System.out.println("披薩打包..");
    }
    
    public String getName() {
        return name;
    }
}

 

披薩店:

/**
 * 只賣一種披薩的披薩店
 * @author skysea
 */
public class PizzaStore {
    
    public Pizza orderPizza() {
        Pizza pizza = new Pizza();
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

 測試類:

/**
 * pizza測試類
 * @author skysea
 */
public class PizzaTest {
    public static void main(String[] args) {
        PizzaStore pizzaStore = new PizzaStore();
        Pizza pizza = pizzaStore.orderPizza();
        System.out.println("當前預定的披薩:" + pizza.getName());
    }
}

 

現在披薩店要拓展業務了,因為賣一種披薩顧客已經吃膩了,現在要開始添加新的披薩類型

簡單工廠模式

Pizza類的改進

import java.util.ArrayList;
import java.util.List;

/**
 * 披薩抽象類
 * 1,修改private -> protected(保證子類擁有這些屬性)
 * 2,將Pizza定義為abstract類,防止被new,也是為後面的改造做準備
 * @author skysea
 */
public abstract class Pizza {
    
    protected String name;//披薩名稱
    
    protected String dough;//麵團

    protected String sauce;//醬料
    
    protected List<String> toppings = new ArrayList<>();//佐料
    
    void prepare() {
        System.out.println("開始準備披薩:" + name);
        
        System.out.print("開始處理麵團:" + dough);
        
        System.out.println("添加醬料:" + sauce);
        
        System.out.println("添加佐料:");
        for(String t : toppings) {
            System.out.println(" " + t);
        }
    }
    
    void bake() {
        System.out.println("烘焙25分鐘..");
    }
    
    void cut() {
        System.out.println("披薩切片..");
    }
    
    void box() {
        System.out.println("披薩打包..");
    }
    
    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Pizza [name=" + name + ", dough=" + dough + ", sauce=" + sauce + ", toppings=" + toppings + "]";
    }
}

先給出新增的披薩

芝士披薩:

/**
 * 芝士披薩
 * @author skysea
 */
public class CheesePizza extends Pizza{
    public CheesePizza() {
        this.name = "芝士披薩";
        this.dough = "芝士披薩的麵團";
        this.sauce = "芝士披薩的醬料";
        this.toppings.add("很多芝士....");
    }
}
View Code

蛤蜊披薩:

/**
 * 蛤蜊披薩
 * @author skysea
 */
public class ClamPizza extends Pizza {
    public ClamPizza() {
        this.name = "蛤蜊披薩";
        this.dough = "蛤蜊披薩的麵團";
        this.sauce = "蛤蜊披薩的醬料";
        this.toppings.add("蛤蜊");
    }
}
View Code

義大利烤腸披薩:

/**
 * 義大利烤腸披薩
 * @author skysea
 */
public class PepperoniPizza extends Pizza{
    
    public PepperoniPizza() {
        this.name = "義大利烤腸披薩";
        this.dough = "義大利烤腸披薩的麵團";
        this.sauce = "義大利烤腸披薩的醬料";
        this.toppings.add("一大波義大利烤腸...");
    }
}
View Code

 素食比薩:

/**
 * 素食比薩
 * @author skysea
 */
public class VeggiePizza extends Pizza {
    public VeggiePizza() {
        name = "素食比薩";
        dough = "素食比薩的麵團";
        sauce = "素食比薩的醬料";
        toppings.add("素食比薩");
        toppings.add("素食比薩佐料1");
        toppings.add("素食比薩佐料2");
    }
}
View Code

貼了這麼多代碼,先給出一波簡單的實現:

/**
 * pizza店
 * @author skysea
 */
public class PizzaStore {

    public Pizza orderPizza(String type) {
        Pizza pizza = null;
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("pepperoni")) {
            pizza = new PepperoniPizza();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();
        } else if (type.equals("veggie")) {
            pizza = new VeggiePizza();
        }
        
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

}

在不考慮繼續擴展披薩種類的時候,這樣的實現有沒有問題,一般來說,達到了可以用的標準,但是不好用,問題如下:

1,沒有相容原來的一種披薩方法 public Pizza orderPizza(),相信我,每一個public方法都是很重要的,因為你不知道有多少地方用到過。當然也不是沒辦法知道,只是你知道也不一定就能改,就算你能改,也不一定改對。

2,String類型的type太容易出錯了,個人感覺對程式開發不友好,當然這個也要分情況,靈活和嚴謹本來就很難做到兩全

3,推薦取不到合適的type時拋異常,而不是返回空,便於排查問題(此處的if裡面只是直接new返回的對象,實際情況遠比現在的複雜)

給出第二版:

/**
 * pizza店
 * @author skysea
 */
public class PizzaStore {
    
    public Pizza orderPizza() {
        return orderPizza(PizzaTypeEnum.CHEESE);
    }
 
    public Pizza orderPizza(PizzaTypeEnum type) {
        Pizza pizza;
 
        pizza = SimplePizzaFactory.getPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }
}

SimplePizzaFactory:

/**
 * 簡單工廠類
 * @author skysea
 */
public class SimplePizzaFactory {

    /**
     * 根據類型獲取pizza
     * @param type
     * @return
     */
    public static final Pizza getPizza(PizzaTypeEnum type){
        switch (type) {
            case CHEESE: return new CheesePizza();
            case CLAM: return new ClamPizza();
            case PEPPERONI: return new PepperoniPizza();
            case VEGGIE: return new VeggiePizza();
            default: throw new NoSuchPizzaException(type.getCode());
        }
    }
}

輔助類(枚舉,異常):

/**
 * 定義pizza類型枚舉
 * @author skysea
 *
 */
public enum PizzaTypeEnum{
    /**
     * 芝士披薩
     */
    CHEESE("cheese"),
    /**
     * 義大利烤腸披薩
     */
    PEPPERONI("pepperoni"),
    /**
     * 蛤蜊披薩
     */
    CLAM("clam"),
    /**
     * 素食比薩
     */
    VEGGIE("veggie");
    private final String code;
    PizzaTypeEnum(String code) {
        this.code = code;
    }
    public String getCode() {
        return code;
    }
}
View Code
/**
 * 沒有匹配的pizza異常
 * @author skysea
 */
public class NoSuchPizzaException extends RuntimeException{
    private static final long serialVersionUID = 6831396172306375611L;
    public NoSuchPizzaException(String message) {
        super(message);
    }
}
View Code

測試類:

/**
 * pizza測試類
 * @author skysea
 */
public class PizzaTest {
 
    public static void main(String[] args) {
        PizzaStore store = new PizzaStore();

        Pizza pizza = store.orderPizza(PizzaTypeEnum.CHEESE);
        System.out.println(pizza);
 
        pizza = store.orderPizza(PizzaTypeEnum.VEGGIE);
        System.out.println(pizza);
    }
}

好了,代碼寫到這裡,其實對於:新增披薩類型的這個需求的實現其實已經很好了。至少來說現階段的需求實現了,其次就是對調用方友好,至少隊友不會跑過來問你類型傳啥,不會告訴你他string字元串傳錯了,不會在你改個類型的時候,還要通知他(當然這個也可以通過常量來處理)。

吹了半天,來說說這段代碼的問題,正常情況下,需求會是這樣變:

1,PepperoniPizza暫時不要了,一般來說,你問他要不要,他會說,這個要看後面的運營情況(我:...)

2,你給我新加一個xx披薩

現在需要改的是兩個地方,一個是工廠類,一個是枚舉,但是主要的流程是不用改了,如果你覺得還是很麻煩在不考慮性能的情況下,你還可以用反射來玩,改造一下工廠類(實現通過class來創建對象)和枚舉(添加一個欄位來存放type對應的class)就可以了,不贅述..

第一波需求就差不多可以這樣收手了,隨著業務的發展,披薩店那叫一個紅火啊,雖然中間也對代碼做了很多新的披薩,但是由於PizzaStore相當穩定,也沒出什麼大問題。

新的問題(開分店):

1,旗艦店在芝加哥,現在要在紐約開一家新的店

2,分店的披薩口味要根據當地的口味來進行調整,保證能夠不失品牌特色的同時,也能滿足當地獨特的風味

3,分店披薩的種類與暫時與旗艦店保持一致

工廠方法模式

先把所有的披薩列出來

芝加哥的披薩:

/**
 * 芝加哥芝士披薩
 * @author skysea
 */
public class ChicagoStyleCheesePizza extends Pizza {

    public ChicagoStyleCheesePizza() { 
        name = "芝加哥芝士披薩";
        dough = "芝加哥芝士披薩麵團";
        sauce = "芝加哥芝士披薩醬料";
 
        toppings.add("芝加哥芝士披薩調料1");
        toppings.add("芝加哥芝士披薩調料2");
    }
 
    @Override
    void cut() {
        System.out.println("芝加哥芝士披薩版切片...");
    }
}
/**
 * 芝加哥蛤蜊披薩
 * @author skysea
 */
public class ChicagoStyleClamPizza extends Pizza {
    public ChicagoStyleClamPizza() {
        name = "芝加哥蛤蜊披薩";
        dough = "芝加哥蛤蜊披薩麵團";
        sauce = "芝加哥蛤蜊披薩醬料";
 
        toppings.add("芝加哥蛤蜊披薩佐料1");
        toppings.add("芝加哥蛤蜊披薩佐料2");
    }
 
    @Override
    void cut() {
        System.out.println("芝加哥蛤蜊披薩版切片...");
    }
}
/**
 * 芝加哥義大利烤腸披薩
 * @author skysea
 */
public class ChicagoStylePepperoniPizza extends Pizza {
    public ChicagoStylePepperoniPizza() {
        name = "芝加哥義大利烤腸披薩";
        dough = "芝加哥義大利烤腸披薩麵團";
        sauce = "芝加哥義大利烤腸披薩醬料";
 
        toppings.add("芝加哥義大利烤腸披薩調料1");
        toppings.add("芝加哥義大利烤腸披薩調料2");
        toppings.add("芝加哥義大利烤腸披薩調料3");
        toppings.add("芝加哥義大利烤腸披薩調料4");
    }
 
    @Override
    void cut() {
        System.out.println("芝加哥義大利烤腸披薩版切片...");
    }
}
/**
 * 芝加哥素食比薩
 * @author skysea
 */
public class ChicagoStyleVeggiePizza extends Pizza {
    public ChicagoStyleVeggiePizza() {
        name = "芝加哥素食比薩";
        dough = "芝加哥素食比薩的麵團";
        sauce = "芝加哥素食比薩的醬料";
 
        toppings.add("芝加哥素食比薩調料1");
        toppings.add("芝加哥素食比薩調料2");
        toppings.add("芝加哥素食比薩調料3");
    }
 
    void cut() {
        System.out.println("芝加哥素食比薩版切片...");
    }
}
View Code

紐約的披薩:

/**
 * 紐約芝士披薩
 * @author skysea
 */
public class NYStyleCheesePizza extends Pizza {

    public NYStyleCheesePizza() { 
        name = "紐約芝士披薩";
        dough = "紐約芝士披薩麵團";
        sauce = "紐約芝士披薩醬料";
 
        toppings.add("紐約芝士披薩調料1");
        toppings.add("紐約芝士披薩調料2");
    }
 
    @Override
    void cut() {
        System.out.println("紐約芝士披薩版切片...");
    }
}
/**
 * 紐約蛤蜊披薩
 * @author skysea
 */
public class NYStyleClamPizza extends Pizza {
    public NYStyleClamPizza() {
        name = "紐約蛤蜊披薩";
        dough = "紐約蛤蜊披薩麵團";
        sauce = "紐約蛤蜊披薩醬料";
 
        toppings.add("紐約蛤蜊披薩佐料1");
        toppings.add("紐約蛤蜊披薩佐料2");
    }
 
    @Override
    void cut() {
        System.out.println("紐約蛤蜊披薩版切片...");
    }
}
/**
 * 紐約義大利烤腸披薩
 * @author skysea
 */
public class NYStylePepperoniPizza extends Pizza {
    public NYStylePepperoniPizza() {
        name = "紐約義大利烤腸披薩";
        dough = "紐約義大利烤腸披薩麵團";
        sauce = "紐約義大利烤腸披薩醬料";
 
        toppings.add("紐約義大利烤腸披薩調料1");
        toppings.add("紐約義大利烤腸披薩調料2");
        toppings.add("紐約義大利烤腸披薩調料3");
        toppings.add("紐約義大利烤腸披薩調料4");
    }
 
    @Override
    void cut() {
        System.out.println("紐約義大利烤腸披薩版切片...");
    }
}
/**
 * 紐約素食比薩
 * @author skysea
 */
public class NYStyleVeggiePizza extends Pizza {
    public NYStyleVeggiePizza() {
        name = "紐約素食比薩";
        dough = "紐約素食比薩的麵團";
        sauce = "紐約素食比薩的醬料";
 
        toppings.add("紐約素食比薩調料1");
        toppings.add("紐約素食比薩調料2");
        toppings.add("紐約素食比薩調料3");
    }
 
    void cut() {
        System.out.println("紐約素食比薩版切片...");
    }
}
View Code

披薩倒是列完了,但是在實際的開發過程中,業務邏輯這麼簡單那是不可能的,想要改那什麼旗艦店披薩的類名是很困難的

一般要考慮:

1,是不是單機,有沒有其他外部系統在調用

2,改動原來的代碼有什麼好處,更容易理解嗎?迭代了幾個版本過後垃圾代碼太多了嗎?

3,影響大不大

當然,我這裡是隨便造,你們呢,我就不知道了,嘿嘿嘿,所以碰到這種情況,一般來說要悠著點,看時間,也要看影響,開發就是這樣,同一個功能,2天有2天的做法,5天有5天的做法,10天有10天的做法

披薩店改造:

/**
 * 披薩店抽象類
 * @author skysea
 */
public abstract class PizzaStore {
 
    abstract Pizza createPizza(String item);
 
    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        System.out.println("--- 製作 " + pizza.getName() + " ---");
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}
/**
 * 芝加哥披薩店
 * @author skysea
 */
public class ChicagoPizzaStore extends PizzaStore {
    public static final String CHEESE = "cheese";
    public static final String VEGGIE = "veggie";
    public static final String CLAM = "clam";
    public static final String PEPPERONI = "pepperoni";
    Pizza createPizza(String item) {
        if (CHEESE.equals(item)) {
            return new ChicagoStyleCheesePizza();
        } else if (VEGGIE.equals(item)) {
            return new ChicagoStyleVeggiePizza();
        } else if (CLAM.equals(item)) {
            return new ChicagoStyleClamPizza();
        } else if (PEPPERONI.equals(item)) {
            return new ChicagoStylePepperoniPizza();
        } else {
            throw new NoSuchPizzaException(item);
        }
    }
}

紐約披薩店(和芝加哥披薩店幾乎一毛一樣,這裡就不展開了):

/**
 * 紐約披薩店
 * @author skysea
 */
public class NYPizzaStore extends PizzaStore {

    public static final String CHEESE = "cheese";
    public static final String VEGGIE = "veggie";
    public static final String CLAM = "clam";
    public static final String PEPPERONI = "pepperoni";
    Pizza createPizza(String item) {
        if (CHEESE.equals(item)) {
            return new NYStyleCheesePizza();
        } else if (VEGGIE.equals(item)) {
            return new NYStyleVeggiePizza();
        } else if (CLAM.equals(item)) {
            return new NYStyleClamPizza();
        } else if (PEPPERONI.equals(item)) {
            return new NYStylePepperoniPizza();
        } else {
            throw new NoSuchPizzaException(item);
        }
    }
}
View Code

這段代碼有三個問題要理解清楚:

1,這個地方為啥要弄個抽象類出來?

這個就要結合實際來理解了,分店與分店之間,需不需要統一規範化管理?需不需要保證自己的特色?答案毫無疑問,都是需要的

這個地方製作披薩的過程,毫無疑問是肯定要一致的。就像外賣一樣,下單,炒菜,配送。整套流程都是這樣,不能說你出來就開始炒菜了啊,這不科學。不一樣的地方就是,你炒的什麼菜,好不好吃。配送得快不快,穩不穩,服務好不好。

所以,抽象類的意義就是:規範、特色

2,factory咋個不見了?

因為把它和具體的store合併在一起了,這樣又引申出另外一個問題:為啥要合併?因為store現在充當的角色就是facotry,剛纔說過的製作過程已經放到父類中實現了,現在只需要在具體的store中去解決披薩的創建問題

3,為啥又不用枚舉了,弄個String來創建pizza?

如果還是單機,用枚舉當然會比直接扔個string來得穩當。

開了分店,要是每個分店都是一套完整的服務在玩,丟個string,要比枚舉來得好。原因有2:傳輸過程中的序列化和反序列化、更加靈活(客戶端不用每次都因為這個原因要去升級對應的包,特別是多個版本在跑得時候,升級了又會導致其他東西不能玩)

測試類:

/**
 * 披薩測試類
 * @author skysea
 */
public class PizzaTest {
 
    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();
 
        Pizza pizza = nyStore.orderPizza(NYPizzaStore.CHEESE);
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza(ChicagoPizzaStore.CHEESE);
        System.out.println("Joel ordered a " + pizza.getName() + "\n");

        pizza = nyStore.orderPizza(NYPizzaStore.CLAM);
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza(ChicagoPizzaStore.CLAM);
        System.out.println("Joel ordered a " + pizza.getName() + "\n");

        pizza = nyStore.orderPizza(NYPizzaStore.PEPPERONI);
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza(ChicagoPizzaStore.PEPPERONI);
        System.out.println("Joel ordered a " + pizza.getName() + "\n");

        pizza = nyStore.orderPizza(NYPizzaStore.VEGGIE);
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");
 
        pizza = chicagoStore.orderPizza(ChicagoPizzaStore.VEGGIE);
        System.out.println("Joel ordered a " + pizza.getName() + "\n");
    }
}

結果(結果太多了,就不全部截圖出來了):

 

 


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

-Advertisement-
Play Games
更多相關文章
  • [1]數據結構 [2]創建棧 [3]使用stack類 [4]ES6 [5]應用 ...
  • 最近在工作中碰到了一個問題,原本在IE8,IE9下正常的input表單,在IE10下會出現清除按鈕,即表單右側會出現一個可以清除該表單內容的小叉。由於之前一直沒有相容過IE10,所以我專門搜了下原因。發現,該功能是微軟在IE10上新添加的功能,用於快速清空表單值。 而問題是,工作中使用到的表單控制項以 ...
  • 之前寫完vue項目後,佈置到伺服器,用nginx反向代理後,一開始進去,進各種路由都是沒問題的,但是一旦f5刷新後就會出現一個nginx404的錯誤。 經過翻閱vue文檔後,發現這是vueHistory 模式下的一個問題,需要後臺配置支持。 History 模式是沒有hash鍵,比如/a/b,f5刷 ...
  • 前言 這裡分享一個博主寫的省市區三級菜單聯動插件 — jQuery Citys,此插件中所有省市區數據均為國家行政區劃代碼,保證數據真實可靠,插件可以根據預設地區代碼或地區名稱進行值的初始化操作。 線上演示地址:https://yangyunhe369.github.io/jQuery Citys ...
  • 無限滾動載入最佳實踐 無限滾動(Infinite scrolling),有時候被稱為無盡滾動(endless scrolling),這種技術允許用戶在大量內容上滾動,眼中看不到結束的地方。這種技術很簡單,就是頁面往下滾動的時候保持刷新。 這項技術使用戶在沒有 打斷 和 額外交互 的情況下滾動列表 — ...
  • Promise API 簡介 譯者註: 到處是回調函數,代碼非常臃腫難看, Promise 主要用來解決這種編程方式, 將某些代碼封裝於內部。 Promise 直譯為“承諾”,但一般直接稱為 Promise; 代碼的可讀性非常重要,因為開發人員支出一般比電腦硬體的支出要大很多倍。 雖然同步代碼更容 ...
  • 在HTML5的規範中,我們可以通過為元素增加`draggable="true"`來設置此元素是否可以進行拖拽操作,其中圖片、鏈接預設是開啟的。 1. 拖拽元素:設置了`draggable="true"`的元素 當拖動某元素時,將依次觸發下列事件: 1. dragstart(按下滑鼠並開始移動滑鼠時, ...
  • 13.Label的作用是什麼?是怎麼用的? label標簽來定義表單控制間的關係,當用戶選擇該標簽時,瀏覽器會自動將焦點轉到和標簽相關的表單事件上。 <label for="Name">Number:</label> <input type="text" name="Name" id="Name"/ ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...