1 Java對(duì)象管理

  則不達(dá)的博客從這篇文章開始,我想說說我為什么寫博客,其實(shí)之前也嘗試過寫博客,但都沒有堅(jiān)持下來,但這回找工作跳槽比較從容,再加上做安卓開發(fā)的這三年,都沒有把知識(shí)體系好好整理一下,一直都是忙工作,正應(yīng)了一個(gè)哥們說的那句話,一年經(jīng)驗(yàn)重復(fù)了三年,當(dāng)初定好的一天一點(diǎn)進(jìn)步的目標(biāo)也沒有實(shí)現(xiàn),所以為了給之前三年一個(gè)交代,也給之后三年一個(gè)好的開端,所以我從今天開始寫文章,這些文章都是一邊學(xué)一邊整理,所以更像是一份筆記。

本節(jié)重點(diǎn)說對(duì)象的創(chuàng)建和管理,如果還有什么這方面的欠缺,歡迎留言。

  • 目錄
    • 靜態(tài)工廠
    • 服務(wù)提供者框架
    • Builder模式的構(gòu)建器
    • 單例模式
    • 簡(jiǎn)單工廠模式
    • 工廠方法
    • 抽象工廠
    • Flyweight:蠅量模式(享元模式)
    • 備忘錄模式
    • 原型模式和深拷貝淺拷貝

Effective Java這本書里,作者一直在申明一條原則:
對(duì)象不能在初始化過程中,還能被訪問到,
必須準(zhǔn)備好所有必要字段,再獲取對(duì)象,
不要留中間狀態(tài)。

1 靜態(tài)工廠

這個(gè)比較簡(jiǎn)單,先上代碼:

1.1 套路


//常規(guī)模式
public static class Boolean{

    //靜態(tài)工廠方法
    public static Boolean valueOf(boolean b){
        return new Boolean(b); 
    }

    private boolean v;
    
    private Boolean(boolean b){
        v = b;
    }
    
}

//高級(jí)模式:限制對(duì)象的數(shù)目,比如單例,此處的例子是:Boolean基本就只能對(duì)應(yīng)兩個(gè)值
public static class Boolean{
        
    //預(yù)定義的兩個(gè)對(duì)象,某種意義上算是緩存,使用時(shí)可以省去創(chuàng)建對(duì)象的過程
    public static final Boolean True = new Boolean(true);
    public static final Boolean False = new Boolean(false);
    
    //靜態(tài)工廠方法
    public static Boolean valueOf(boolean b){
        return b ? True : False;
    }

    private boolean v;
    
    private Boolean(boolean b){
        v = b;
    }
    
}
  • 靜態(tài)工廠方法的好處是:

    • 1 方法有名字,構(gòu)造器沒有,所以可以給出不同重載,而方法名賦予其意義,這點(diǎn)就勝于構(gòu)造器
    • 2 可以在靜態(tài)工廠里控制返回的對(duì)象,不一定非得是new出來的
    • 3 可以返回任意子類型的對(duì)象,返回的父類或者接口引用,具體實(shí)現(xiàn)類甚至可以對(duì)外隱藏,參考Java Collections Framework中集合接口的32個(gè)便利實(shí)現(xiàn)
      • (1)Collections里有unmodified, empty, checked, synchronized各8個(gè)方法,對(duì)應(yīng)8種不同的集合類型
      • 這8種集合類型就是:Collection, List, Map, SortedMap, NavigableMap, Set, SortedSet, NavigableSet,注意這是接口類型,對(duì)外的
      • 返回的實(shí)際類型是什么呢,都是以private static class的形式實(shí)現(xiàn)的,并未對(duì)外公開,所以可以jdk隨時(shí)修改,提升性能或修改實(shí)現(xiàn)
      • (2)EnumSet:其靜態(tài)工廠方法會(huì)根據(jù)底層枚舉類型大小,返回RegalarEnumSet對(duì)象或者JumboEnumSet對(duì)象,而且這對(duì)外部用戶是隱藏的
    • 4 簡(jiǎn)化Map<String, String> m = new HashMap<String, String>()這種繁瑣的調(diào)用, Map<String, String> m = HashMap.newHashMap();
  • 靜態(tài)工廠的命名有幾個(gè)套路,可以讓別人一看到方法名,就知道是工廠方法

    • 靜態(tài)工廠方法終究也只是普通的static方法,文檔中并不會(huì)特別對(duì)待,這是個(gè)缺點(diǎn)
    • 所以這里有些命名套路,還是應(yīng)該遵守的
    • valueOf:類似Boolean.valueOf(),實(shí)際是類型轉(zhuǎn)換,參數(shù)和返回的實(shí)例有相同的值
    • of:valueOf的簡(jiǎn)潔寫法,在EnumSet中流行起來
    • getInstance:參數(shù)可選
    • newInstance:隱含的意思是每次都是new一個(gè)新的
    • getType, newType

2 服務(wù)提供者框架

這是從靜態(tài)工廠好處3里引出來的,對(duì)外提供一個(gè)接口,客戶端依賴于此接口實(shí)例,但并不關(guān)心具體實(shí)現(xiàn)

  • 三大組件:以JDBC為例
    • 服務(wù)接口:Service Interface,提供者實(shí)現(xiàn),如Connection
    • 提供者注冊(cè)API:Provider Registreation API,用來注冊(cè)實(shí)現(xiàn),讓客戶端訪問,如DriverManager.registerDriver()
    • 服務(wù)訪問API:Service Access API,客戶端用來獲取服務(wù)實(shí)例,這里就是靈活的靜態(tài)工廠,如DriverManager.getConnection()
    • 服務(wù)提供者接口,Service Provider Interface,可選,用來創(chuàng)建服務(wù)實(shí)例,如果沒有這個(gè),就得按照類名注冊(cè),并通過反射實(shí)例化,如Driver就是這個(gè)角色

/**
 * ================服務(wù)接口:Service Interface==================
 * 一個(gè)對(duì)外提供服務(wù)的接口,并且不同情況,會(huì)產(chǎn)生不同的Service對(duì)象,
 * 即通過Service的不同實(shí)現(xiàn),對(duì)外提供不同的服務(wù)
 *
 */
public interface Service {
    
    void doService();

}


/**
 * ================服務(wù)提供者接口==================
 * 用來生成Service對(duì)象,注意,如果不使用Provider,則注冊(cè)到Services的就得是Service實(shí)現(xiàn)類的Class對(duì)象,
 * newInstance也只能通過反射來了
 * 問題就是Provider實(shí)現(xiàn)類應(yīng)該有幾個(gè)
 *
 */
public interface Provider {
    Service newService();
}

public class Services {

    private Services(){}
    
    //================提供者注冊(cè)API==================//
    //這里要么注冊(cè)provider對(duì)象,要么注冊(cè)Service實(shí)現(xiàn)類的Class,你選吧
    private static final Map<String, Provider> providers = new ConcurrentHashMap<>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";
    
    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }

    public static void registerProvider(String defaultProviderName, Provider p) {
        providers.put(defaultProviderName, p);
    }
    
    //================服務(wù)訪問API==================//
    public static Service newInstance(){
        return newInstance(DEFAULT_PROVIDER_NAME);
    }

    public static Service newInstance(String name) {
        Provider p = providers.get(name);
        if(p == null){
            throw new IllegalArgumentException("No provider registered with name + " + name);
        }
        return p.newService();
    }
}

  • 使用場(chǎng)景
    • Service提供了某項(xiàng)工作的接口
    • Services是一個(gè)平臺(tái):
      • 注冊(cè)接口:用于注冊(cè)Service的實(shí)現(xiàn)
        • 可以注冊(cè)Provider,如上例,規(guī)避調(diào)用Class對(duì)象和反射
        • 可以注冊(cè)Service實(shí)現(xiàn)類的Class對(duì)象
        • 可以注冊(cè)Service實(shí)現(xiàn)類的實(shí)例
        • 訪問接口:用于獲取Service的實(shí)現(xiàn)的實(shí)例
      • api作者和用戶都可以實(shí)現(xiàn)Service和Provider,即提供服務(wù)
      • JDBC的服務(wù)就是連接數(shù)據(jù)庫(kù),不同的Service實(shí)現(xiàn),對(duì)應(yīng)不同的數(shù)據(jù)庫(kù),也使用了不同的驅(qū)動(dòng)

3 Builder模式的構(gòu)建器

  • 使用場(chǎng)景:參數(shù)個(gè)數(shù)多
    • 原始模式:構(gòu)造器或者靜態(tài)工廠形式太多,參數(shù)多
    • JavaBeans模式:new一個(gè)空對(duì)象然后set各種參數(shù),代碼不宜管理,set過程中,對(duì)象也可能處于不一致狀態(tài)
    • 把JavaBeans的一組set封裝起來,就是一個(gè)建造者模式的原始形態(tài),但里面依舊需要對(duì)對(duì)象的一致性負(fù)責(zé)
      • 這里說的一致性問題,意思是new完對(duì)象,還需要一組set之后,對(duì)象才能正常工作,
      • 但set期間,對(duì)象已經(jīng)有了,卻不能正常工作,這就是一個(gè)危險(xiǎn)的狀態(tài)
      • Builder模式,就不存在這個(gè)不一致的狀態(tài),因?yàn)閷?duì)象最終還是通過一個(gè)構(gòu)造器出來后就已經(jīng)可以正常工作了
    • 所以這時(shí)使用Builder模式,既能保證JavaBeans的可讀性,又能保證原始模式的安全性
    • ImageLoader的初始化,AlertDialog的初始化,都使用了這種模式,參數(shù)很多,有些必填(放到Buidler的構(gòu)造方法),有些選填(作為單獨(dú)方法)

3.1 簡(jiǎn)單模式

public class NutritionFacts {

    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;
    
    
    public NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
    
    public static class Builder{
        
        //必填的參數(shù),無默認(rèn)值
        private final int servingSize;
        private final int servings;
        
        //選填的參數(shù),有默認(rèn)值
        private int calories = 0;
        private int fat      = 0;
        private int carbohydrate = 0;
        private int sodium = 0;
        
        public Builder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings = servings;
        }
        
        public Builder calories(int val){ calories = val; return this; }
        public Builder fat(int val){ fat = val; return this; }
        public Builder carbohydrate(int val){ carbohydrate = val; return this;}
        public Builder sodium(int val){ sodium = val; return this; }
    
        public NutritionFacts build(){
            return new NutritionFacts(this);
        }
        
    }
    
}

public static void main(String[] args) {
    NutritionFacts cocacola = new NutritionFacts.Builder(240,  8)
            .calories(100)
            .sodium(35)
            .carbohydrate(27)
            .build();
}


3.2 Builder接口模式

將Builder抽取出來單獨(dú)做一個(gè)接口,這個(gè)接口的對(duì)象可以:

  • 創(chuàng)建任意多的對(duì)象
  • 其功能就類似于直接傳Class對(duì)象
  • 但比Class對(duì)象的newInstance方法多了類型檢查,構(gòu)造方法保證等
  • 缺點(diǎn)就是要?jiǎng)?chuàng)建N多個(gè)Builder類
public interface Builder<T> {
    public T build();
}


public class NutritionFacts2 {

    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;
    
    
    public NutritionFacts2(MyBuilder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
    
    public static class MyBuilder implements Builder<NutritionFacts2>{
        
        //必填的參數(shù),無默認(rèn)值
        private final int servingSize;
        private final int servings;
        
        //選填的參數(shù),有默認(rèn)值
        private int calories = 0;
        private int fat      = 0;
        private int carbohydrate = 0;
        private int sodium = 0;
        
        public MyBuilder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings = servings;
        }
        
        public MyBuilder calories(int val){ calories = val; return this; }
        public MyBuilder fat(int val){ fat = val; return this; }
        public MyBuilder carbohydrate(int val){ carbohydrate = val; return this;}
        public MyBuilder sodium(int val){ sodium = val; return this; }
    
        public NutritionFacts2 build(){
            return new NutritionFacts2(this);
        }
        
    }
    
    public static void main(String[] args) {
        Builder<NutritionFacts2> builder = new NutritionFacts2.MyBuilder(240,  8)
                .calories(100)
                .sodium(35)
                .carbohydrate(27);
        
        NutritionFacts2 cocacola = builder.build();
    }
}

/*
這里的Builder<NutritionFacts2> builder對(duì)象,可以傳給任意的抽象工廠方法
*/

4 單例模式

  • 怎么能破壞單例的限制
    • 反射:反射出私有構(gòu)造方法,Enum可自然規(guī)避此問題,其他方式得強(qiáng)寫檢查代碼
    • 序列化:將單例序列化,再反序列化,出來就是一個(gè)新對(duì)象,Enum可自然解決,其他方式使用readResolve方法
    • 安卓里的多進(jìn)程,會(huì)產(chǎn)生多個(gè)Application

4.1 餓漢:公有域,或靜態(tài)工廠

public class Singleton {
    
    public static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    private Object readResolve(){ return INSTANCE; }
    
    public void provideService(){
        
    }

}

//訪問
Singleton.INSTANCE.provideService();
public class Singleton {
    
    private static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){ return INSTANCE; }
    private Object readResolve(){ return INSTANCE; }
    
    public void provideService(){
        
    }

}

//訪問
Singleton.getInstance().provideService();

4.2 懶漢:雙保險(xiǎn)模式

這里注意延遲加載和volatile的使用

public 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;
   }
}

這雙重檢查的概念可以推廣到所有需要延遲加載的地方,如果一個(gè)變量需要延遲加載,
那訪問的時(shí)候,你就應(yīng)該這樣:


private volatile T field;

T getField(){
    //用到了result這個(gè)局部變量,確保field只在已經(jīng)被初始化的情況下讀取一次,可以提升性能(非必須)
    //據(jù)說 速度比不用局部變量快了25%
    T result = field;  
    if(result == null){
        synchronized(this){
            result = field;
            if(result == null){
                field = result = computeFieldValue();
            }
        }
    }
    return result;
}

4.3 懶漢:內(nèi)部類模式

這里的說法是:
SingletonHolder作為一個(gè)內(nèi)部類,會(huì)在訪問時(shí)被加載,所以這里實(shí)現(xiàn)了延遲加載,并且內(nèi)部類可以從語(yǔ)言層面上防止多線程的問題,比雙重鎖模式優(yōu)雅的多。

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

訪問Singleton這個(gè)類時(shí),才加載Inner,單例才被初始化
而且一個(gè)Inner保證了有一個(gè)singleton靜態(tài)實(shí)例
那能保證Inner只有一個(gè)嗎?能啊,ClassLoader會(huì)保證Inner的Class就一個(gè)

4.4 枚舉

按照Effective Java書里說法,這個(gè)方法雖然沒流行起來,但最符合單例的需求

//直接就能防止反射,防止序列化時(shí)生成新類
public enum Singleton {
    
    INSTANCE;
    
    public void provideService(){
        
    }

}

4.5 暴力反射版的單例:趣味探索

public class Singleton {
    private Singleton(){}
    public void doSth(){
        System.out.println("做點(diǎn)什么");
    }
}
public class SingletonFactory {
    private static Singleton singleton;
    //===只實(shí)例化一次,使用暴力反射
    static{
        try {
            Class cls = Class.forName(Singleton.class.getName());
            Constructor cons = cls.getDeclaredConstructor();
            cons.setAccessible(true);
            singleton = (Singleton) cons.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static Singleton getSingleton(){
        return singleton;
    }
    
    /**
     * 擴(kuò)展:一個(gè)項(xiàng)目可以有一個(gè)單例構(gòu)造器,負(fù)責(zé)生成所有單例對(duì)象,只需要傳入類型,
     * 但是需要事先知道有幾個(gè)單例類型
     */
}

5 簡(jiǎn)單工廠模式

場(chǎng)景:披薩店里生產(chǎn)各種披薩,需要你實(shí)現(xiàn)一個(gè)用戶點(diǎn)餐的接口,用戶可以選擇披薩類型

先給出Pizza的類體系

public abstract class Pizza{

}

public class CheesePizza extends Pizza{

}

public class PepperoniPizza extends Pizza{

}

public class ClamPizza extends Pizza{

}

public class VeggiPizza extends Pizza{

}
//------------------------------------------------------------
//第一個(gè)例子,不用工廠模式
//------------------------------------------------------------
Pizza orderPizza(String type){
    Pizza pizza;
    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 VeggiPizza();
    }
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}
//如果需要增刪pizza的種類,則這個(gè)方法的代碼需要修改
//也就是需要修改已經(jīng)編譯好的
//不符合對(duì)修改關(guān)閉,對(duì)擴(kuò)展開放的原則

//------------------------------------------------------------
//第二個(gè)例子,使用簡(jiǎn)單工廠模式
//------------------------------------------------------------
public class SimplePizzaFactory{
    
        ///方式1:傳入字符串類型的type,或者int類型的type,依然需要修改,但至少代碼已經(jīng)都?xì)w到一個(gè)地方了
    public Pizza createPizza(String type){
        Pizza pizza;
        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 VeggiPizza();
        }
        return pizza;
    }
        
        ///方式2:再增加新的pizza類型,這個(gè)方法也不需要修改了
        public <T extends Pizza> T createPizza(Class<T> clazz){
        Pizza pizza;
        try{
            pizza = clazz.newInstance();
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("生成錯(cuò)誤");
        }
        return (T)pizza;
    }

       ///方式3:參數(shù)也可以傳入Builder,Provider等
    
}

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

6 工廠方法

需要注意的是,工廠方法不是簡(jiǎn)單工廠的升級(jí),
而是簡(jiǎn)單工廠所對(duì)應(yīng)的需求升級(jí)了,這里的升級(jí)就是,出現(xiàn)了不同的產(chǎn)品等級(jí)
Pizza店生意好了,采用了加盟模式,NewYork, Chicago都有加盟店了,
但是這兩個(gè)地方的Pizza風(fēng)味有所不同,雖然同是Chesse或者Pepper,但是
還都有各自的風(fēng)味,如餅的厚薄,餅的大小,傳入同樣的type,但返回的對(duì)象不一樣了

工廠方法提供一個(gè)工廠接口或基類,由子類完成具體的創(chuàng)建動(dòng)作,
所以工廠基類中,對(duì)對(duì)象的處理,就和子類的創(chuàng)建動(dòng)作解耦了

工廠方法,能讓各地的Pizza店還保持一樣的制作流程,但各自在創(chuàng)建時(shí),允許發(fā)揮個(gè)性


public abstract class PizzaStore{
        public Pizza orderPizza(String type){
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    abstract Pizza createPizza(String type);
}
public class NewYorkPizzaStore extends PizzaStore{
    
    public Pizza createPizza(String type){
        Pizza pizza;
        if(type.equals("cheese")){
            pizza = new NYCheesePizza();
        }else if(type.equals("pepperoni")){
            pizza = new NYPepperoniPizza();
        }else if(type.equals("clam")){
            pizza = new NYClamPizza();
        }else if(type.equals("veggie")){
            pizza = new NYVeggiPizza();
        }
        return pizza;
    }
    
}
public class ChicogoPizzaStore extends PizzaStore{
    
    public Pizza createPizza(String type){
        Pizza pizza;
        if(type.equals("cheese")){
            pizza = new CGCheesePizza();
        }else if(type.equals("pepperoni")){
            pizza = new CGPepperoniPizza();
        }else if(type.equals("clam")){
            pizza = new CGClamPizza();
        }else if(type.equals("veggie")){
            pizza = new CGVeggiPizza();
        }
        return pizza;
    }
    
}

Pizza的類體系還是差不多

public abstract class Pizza{

}

public class NYCheesePizza extends Pizza{

}

public class NYPepperoniPizza extends Pizza{

}

public class CGCheesePizza extends Pizza{

}

public class CGPepperoniPizza extends Pizza{

}
  • 分析
    • 一樣的type,但是不同的工廠子類返回了不同的類型
    • 比如說螺絲分為大小螺絲,可以有大螺絲工廠,小螺絲工廠,大和小就是產(chǎn)品等級(jí)
    • 而螺絲,螺帽系列,就是一個(gè)產(chǎn)品族,對(duì)應(yīng)到pizza上,就是pizza,辣椒,調(diào)料就是一個(gè)產(chǎn)品族
    • 涉及到產(chǎn)品族,就需要用到抽象工廠了
這里給出一段代碼,還是要解決上面的需求,但不使用工廠方法,只用暴力方案

public class DependentPizzaStore{
    
    public Pizza createPizza(String style, String type){
        Pizza pizza = null;
        if(style.equals("NewYork")){
            if(type.equals("cheese")){
                return NYCheesePizza();
            }else if(type.equals("veggi")){
                return NYVeggiPizza();
            }
        }else if(style.equals("Chicago")){
            if(type.equals("cheese")){
                return CGCheesePizza();
            }else if(type.equals("veggi")){
                return CGVeggiPizza();
            }
        }
        return pizza;
    }
    
}

這段代碼很重要,很直觀的告訴你簡(jiǎn)單工廠和工廠方法解決的問題

createPizza(String style, String type)兩個(gè)參數(shù)時(shí),style就變成了工廠方法里的類體系結(jié)構(gòu),每個(gè)工廠子類其實(shí)就是不同的style

createPizza(String type)一個(gè)參數(shù)時(shí),使用簡(jiǎn)單工廠就可以了

如果出現(xiàn)了CreatePepper, CreateSauce,需要你創(chuàng)建辣椒和醬料,和Pizza一起作為一個(gè)產(chǎn)品族,就用到抽象工廠了

7 抽象工廠

看到這里時(shí),你應(yīng)該已經(jīng)了解了簡(jiǎn)單工廠和工廠方法

  • 你應(yīng)該已經(jīng)知道了
    • 同一個(gè)東西的不同風(fēng)格,是產(chǎn)品等級(jí)不同,如大小螺絲,需要不同風(fēng)格的工廠--工廠方法
    • 不同的東西形成一個(gè)系列,如螺絲和螺帽,是一個(gè)產(chǎn)品族,在一個(gè)工廠里生產(chǎn)
現(xiàn)在假設(shè)不同地域的pizza店,得就近生產(chǎn)自己地區(qū)的醬料和奶酪,以保持新鮮


public interface AbstractFactory{
    public Cheese createCheese();
    public Sauce createSauce();
}

public class NewYorkFactory extends AbstractFactory{
    public Cheese createCheese(){
        return NYCheese();
    }
    public Sauce createSauce(){
        return NYSauce();
    }
}
public class CGFactory extends AbstractFactory{
    public Cheese createCheese(){
        return CGCheese();
    }
    public Sauce createSauce(){
        return CGSauce();
    }
}


這倆工廠用在哪兒呢?注意,醬料和奶酪作為一個(gè)產(chǎn)品族,他們組裝出來的產(chǎn)品就是:Pizza

所以這里的工廠,應(yīng)該用在組裝Pizza的地方
public class ChicogoPizzaFactory extends PizzaFactory{
    
    public Pizza createPizza(String type){
        Pizza pizza;
                AbstractFactory fac = new CGFactory();
        if(type.equals("cheese")){
            pizza = new CGCheesePizza(fac);
        }else if(type.equals("pepperoni")){
            pizza = new CGPepperoniPizza(fac);
        }else if(type.equals("clam")){
            pizza = new CGClamPizza(fac);
        }else if(type.equals("veggie")){
            pizza = new CGVeggiPizza(fac);
        }
        return pizza;
    }
    
}
  • 三個(gè)工廠模式的總結(jié):
    • 簡(jiǎn)單工廠:解決了單個(gè)店面的選擇不同類型pizza的問題
    • 工廠方法:解決了不同地域Pizza店的口味問題,同一類型的pizza可以做出不同風(fēng)格
    • 抽象工廠:解決了不同地域的Pizza原料生產(chǎn)問題,原料作為產(chǎn)品族
    • 這里Pizza的例子擴(kuò)展到女媧造人的場(chǎng)景里
      • 人的類型:男人,女人---由簡(jiǎn)單工廠解決,SimpleFactory.createHuman(type)
      • 人的不同風(fēng)格:黃色,黑色,白色--由工廠方法解決
        • IHumanFactory.createHuman(type)
        • 分為YellowFactory,BlackFactory,WhiteFactory
      • 人的零件問題:胳膊,腿,腦袋
        • AbstractFactory.create胳膊,create腿,create腦袋
        • Yellow零件Factory,Black零件Factory,White零件Factory
        • 而抽象工廠,需要設(shè)置給上面不同膚色的工廠方法使用,來組裝成不同膚色的人

8 Flyweight:蠅量模式(享元模式)

  • 場(chǎng)景:
    • 一個(gè)森林里有很多樹,樹有千萬(wàn)棵,但一共就三種(柳樹,槐樹,楊樹),然后就是樹的位置不同,樹的樣子是外部狀態(tài),樹的位置是內(nèi)部狀態(tài)
    • 一個(gè)年級(jí)很多Student,各個(gè)Student保存一個(gè)Class班級(jí)信息,其實(shí)1000個(gè)學(xué)生,就10個(gè)Class,Class是外部狀態(tài),其他如姓名,生日,成績(jī),是內(nèi)部狀態(tài)
    • 一個(gè)對(duì)象很多字段,但有一組字段是所有對(duì)象都一樣的,1000個(gè)對(duì)象,可能這一組字段就幾種情況,這一組字段,就是外部狀態(tài),其他的字段,是內(nèi)部狀態(tài)

Flyweight模式就是解決外部狀態(tài)的問題,用一個(gè)對(duì)象,提供很多個(gè)虛擬對(duì)象


//樹的對(duì)象千千萬(wàn),但不怕,這里走了個(gè)極端,樹成了無狀態(tài)對(duì)象,其實(shí)可以有內(nèi)部狀態(tài),
//如位置xy和樹齡age,都可以作為內(nèi)部狀態(tài)使用,而非方法參數(shù)
public class Tree{
    RealTree realTree;
    public void display(int x, int y, int age){

    }
}

///樹的外部狀態(tài),其實(shí)就三個(gè)對(duì)象,柳樹,楊樹,槐樹
//這里這幾個(gè)字段說是固定不變的,可能有點(diǎn)牽強(qiáng),不要太糾結(jié)
public class RealTree{
    public Leaf leaf;
    public Type type;
    public int 硬度;
    public int 果實(shí);
}


public class TreeManager{
    public HashMap<String, RealTree> flyweights = ...;
    static{
      flyweights.put("楊樹", new RealTree());
      ....
    }
    public RealTree getRealTree(String key){...}
    
}

9 備忘錄模式

對(duì)象快照,例如在命令模式的撤銷功能中,需要記住對(duì)象之前的狀態(tài),或者游戲的進(jìn)度保存

備忘錄模式就是由對(duì)象自己管理自己的save和restore

public class GameRole{
    
    private String currentEnemy;
    private String currentFriend;
    private int currentX, currentY;
    private int currentAttachPower;
    
    public Memento createMemento(){
        return new Memento(currentX, currentY);
    }
    
    public void setMemento(Memento memento){
        this.currentX = memento.currentX;
        this.currentY = memento.currentY;
    }
}

public static class Memento{
    private int currentX, currentY; //當(dāng)前位置坐標(biāo),需要保存和還原
}

10 原型模式和深拷貝淺拷貝

深拷貝與淺拷貝問題中,會(huì)發(fā)生深拷貝的有java中的8種基本類型以及他們的裝箱類型,另外還有String類型。其余的都是淺拷貝

public class Prototype implements Cloneable {  
    private ArrayList list = new ArrayList();  
    public Prototype clone(){  
        Prototype prototype = null;  
        try{  
            prototype = (Prototype)super.clone();  
            prototype.list = (ArrayList) this.list.clone();  
        }catch(CloneNotSupportedException e){  
            e.printStackTrace();  
        }  
        return prototype;   
    }  
}
  • 你需要弄清楚的問題:
    • 你的對(duì)象到底要對(duì)任何一個(gè)域拷貝到什么程度
    • 域的類型是否實(shí)現(xiàn)了clone,無論是你實(shí)現(xiàn)還是JDK實(shí)現(xiàn),怎么實(shí)現(xiàn)的,你得知道
    • 就上例而言:由于ArrayList不是基本類型,所以成員變量list,不會(huì)被拷貝,需要我們自己實(shí)現(xiàn)深拷貝,幸運(yùn)的是java提供的大部分的容器類都實(shí)現(xiàn)了Cloneable接口。所以實(shí)現(xiàn)深拷貝并不是特別困難。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,740評(píng)論 18 399
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,846評(píng)論 25 708
  • 拒絕在我看來是非常難的,拒絕之后還能保持良好的關(guān)系,懂得拒絕的人肯定有著非一般的情商。 我把拒絕分成拒絕要求和拒絕...
    綠兔子000閱讀 388評(píng)論 2 0
  • 在百度推出綠蘿算法之后,不少團(tuán)隊(duì)幾乎放棄了對(duì)外鏈的投入。要知道,在未推出綠蘿算法之前,很多公司的SEO團(tuán)隊(duì)幾乎都會(huì)...
    艾米要奮進(jìn)閱讀 2,379評(píng)論 1 6
  • 一次朋友聚會(huì),閑聊中,兩位媽媽提起最近想給孩子買一輛童車,這個(gè)話題一拋出,引起一路熱議,大家紛紛談起自己的經(jīng)驗(yàn),拿...
    yo小丟兒閱讀 208評(píng)論 0 0