EffectiveJava----靜態工廠方法(我認爲應該叫靜態構造器方法)

一個模塊的使用者永遠也不應該被模塊的行爲所迷惑(那樣就不清晰了),模塊要儘可能的小,但又不能太小【術語模塊(module):是指任何可重用的軟件組件,從單個方法到包含多個包的複雜系統都可以是一個模塊】。代碼應該被重用,而不是拷貝,模塊之間的相依性應該儘可能降低到最小,錯誤應該儘早被檢測出來,理想的情況下是在編譯的時刻。

1.用靜態工廠方法代替構造器(構造函數)
  1. 靜態工廠方法與設計模式中的工廠模式是不同的。
    • 靜態工廠方法是獲取這個類自身的一個實例,他的存在是爲了更好的描述和處理這個類
    • 工廠模式的作用更在於解耦,讓每個類在實例化的時候不再使用new這種耦合度極高的方法。
    • 靜態工廠方法只是一個“普通的方法”(返回這個對象的一個實例這種特點的靜態方法)
    • 工廠方法又分爲三種模式:簡單工廠模式、工廠模式(工廠方法模式)、抽象工廠模式。這三種是爲了一個目標的不同程度的抽象。工廠方法用一種比靜態工廠方法更加系統的理論來對每一個需要使用的對象進行包裝這種包裝下再也不使用new來創建對象,隨着抽象程度的變高,我們甚至可以將這種創建對象放到xml或者註釋之中,就比如一些框架。
    • 靜態工廠方法在一個類的內部,較小的範圍裏可以讓你創建對象更加方便優雅,而工廠模式在大的範圍中能夠讓你的代碼重用性更佳,耦合度更低。
  2. 靜態工廠方法與構造器最大的不同 在於它有名稱。使用靜態工廠方法,爲不同的構造方法來起不同的名字來區分不同功能的實例化,而返回的都是一個this。
    • 比如 valueOf、newInstance、getInstance 等,對於代碼的編寫和閱讀都能夠更清晰。 Calendar.getInstance(); Integer.valueOf(“3”);
    • 而不是new 各種構造器
  3. 靜態工廠方法可以返回一個現有的實例。
    • 我們使用new來初始化一個對象時,都無疑是在堆上創建了一個新的對象
    • 有些不可變的類、不希望被實例出多個對象的類不用也不必每次都創建一個對象。通過靜態工廠方法,我們可以直接向客戶端返回一個我們早已創建好的對象,對於有些不可變的類,比如基本類型包裝類,這樣做可以極大地節省我們的開銷。
  4. 靜態工廠方法可以返回原返回類型的任何子類型的對象。(通過返回一個List(或者其他具體類型的祖先類,Set,Map等)對象,讓那些List的子類都可以來調用這個方法。)

    Class Person {
        public static Person getInstance(){
            return new Person();
            // 這裏可以改爲 return new Player() / Cooker()
        }
    }
    Class Player extends Person{
    }
    Class Cooker extends Person{
    }
    
  5. 可以有多個參數相同但名稱不同的工廠方法

    class Child{
            int age = 10;
            int weight = 30;
            public static Child newChild(int age, int weight) {
                Child child = new Child();
                child.weight = weight;
                child.age = age;
                return child;
            }
            //  完美解決了  構造函數 重載無法解決的問題。
            public static Child newChildWithWeight(int weight) {
                Child child = new Child();
                child.weight = weight;
                return child;
            }
            public static Child newChildWithAge(int age) {
                Child child = new Child();
                child.age = age;
                return child;
            }
        }
    
  6. 可以減少對外暴露的屬性

    class Player {
        public static final int TYPE_RUNNER = 1;
        public static final int TYPE_SWIMMER = 2;
        public static final int TYPE_RACER = 3;
        int type;
    
        private Player(int type) {
            this.type = type;
        }
    
        public static Player newRunner() {
            return new Player(TYPE_RUNNER);
        }
        public static Player newSwimmer() {
            return new Player(TYPE_SWIMMER);
        }
        public static Player newRacer() {
            return new Player(TYPE_RACER);
        }
    }
    
  7. 多了一層控制,方便統一修改儘量封裝起來 是代碼看的更簡潔,看個人習慣問題 對於可能被多個地方引用的類和對象 封裝是爲了更好的擴展 因爲封裝起來後 以後改動只需要改一個地方。

     class User{
            String name ;
            int age ;
            String description;
            public static User newTestInstance() {
                User tester = new User();
                tester.setName("隔壁老張");
                tester.setAge(16);
                tester.setDescription("我住隔壁我姓張!");
                return tester;
            }
        }
        // 創建一個測試數據
        User tester = User.newTestInstance();
    
2. 工廠模式
  1. 簡單工廠 工廠類中,根據條件決定一個接口由哪個具體產品類來實現。注意簡單工廠 和上面的靜態工廠方法不一樣 其實我認爲上面不應該叫靜態工廠方法 應該叫靜態構造器方法(通過專門定義一個類來負責創建其他類的實例,被創建的實例通常都具有共同的父類)
  2. 工廠方法 創建多個工廠類。各個工廠類中,都對應一個獲得接口A實例的方法。用戶決定使用哪個工廠。工廠方法將類的實例化推遲到了其子類。所以使用工廠方法模式時,需要客戶端決定實例化哪一個工廠類。選擇判斷問題還是存在的。也就是說,工廠方法把簡單的工廠內部邏輯判斷轉移到了客戶端來運行。你想要加的功能,本來是要改工廠類的,而現在是修改客戶端。不過,我們在某些情況下通過工廠方法,只需要修改一行實例化的代碼就可以實現系統元素的切換(比如切換數據源)。這也是很方便的。
  3. 抽象工廠:對工廠方法進行擴展。各個工廠類中,再增加一個獲得接口B實例的方法。抽象工廠爲不同產品族的對象創建提供接口。使用場景系統需要在不同產品族進行切換
    • 抽象工廠最大的好處就是便於交換產品系列,具體工廠在代碼中一般只出現一次。這就使得改變應用的具體工廠很容易。
    • 第二個好處是他能讓具體的創建對象實例和客戶端分離,客戶端是通過他們的抽象接口操作實例
    • 抽象工廠不太易於拓展,如果需要自增功能,或者自增產品,則需要至少修改三個類,而且實例化的代碼是寫死在程序中的 , 這樣無法避免違背開放-關閉原則。
    • 對於上述問題,可以通過配置文件,結合反射的方式來解決
  4. 工廠類可以繼承於某個接口,或是抽象類,工廠類已經對產品類的實現就行了封裝,用戶用它結合配置參數和反射實現動態創建,是很合理的。相比簡單工廠是不太合適的。
  5. 代碼底層,當產品類創建分支是固定或是其他類似的地方很少時,用簡單工廠很合適。因爲一旦增加分支,改的地方很少。如果不是,建議用工廠方法。
  6. 抽象工廠和工廠方法沒有本質區別,是對工廠方法的擴展。當產品類,涉及到多個產品簇時,需要對同類的產品抽象爲一個接口。工廠類中,可以定義多個返回具體產品的方法,自由組合。

簡單工廠

    public abstract class Operation {

        public abstract float getResult(float firstNumber, float secondNumber);

    }
    //把符號都當做對象處理,實現此接口
    public class AddOperation extends Operation {
        @Override
        public float getResult(float firstNumber, float secondNumber) {
            return firstNumber+secondNumber;
        }

    }
    public class SubOperation extends Operation {
        @Override
        public float getResult(float firstNumber, float secondNumber) {
            return firstNumber-secondNumber;
        }
    }
    public class MulOperation extends Operation {
        @Override
        public float getResult(float firstNumber, float secondNumber) {
            return firstNumber*secondNumber;
        }
    }
    public class DivOperation extends Operation {
        @Override
        public float getResult(float firstNumber, float secondNumber) {
            return firstNumber/secondNumber;
        }
    }

    //接下來需要解決的就是對象的創建問題了,既如何根據不同的情況創建不同的對象:我們正好可以通過簡單工廠模式實現
    public class OperationFactory {

        public static Operation getOperation(String quotaFlag){
            Operation o = null;
            switch (quotaFlag){
                case "+" :  o = new AddOperation();
                case "-" :  o = new SubOperation();
                case "*" :  o = new MulOperation();
                case "/" :  o = new DivOperation();
                default:break;
            }
            return o;
        }
    }
    //調用:
    public class Computer {
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            System.out.println("請輸入第一個數字:");
            float firstNum  = in.nextFloat();
            System.out.println("請輸入第二個數字:");
            float secondNum  = in.nextFloat();
            System.out.println("請輸入運算符號:");
            String countQuato = in.next();
            System.out.println(count(firstNum,secondNum,countQuato));
        }
        private static float count(float firstNum,float secondNum , String countQuota){
        //通過工廠類獲取對象
            Operation operation = OperationFactory.getOperation(countQuota);
            return operation.getResult(firstNum,secondNum);
        }
    }

工廠

    //定義上級工廠的接口
    public interface IFractory {
        public Operation generateOper();
    }
    //爲每一個類創建工廠
    /**
     * 工廠方法  爲每個對象生成一個工廠類
     */
    public class AddOperationFactory implements IFractory{

        @Override
        public Operation generateOper() {
            return new AddOperation();
        }
    }
    public class SubOperationFactory implements IFractory {
        @Override
        public Operation generateOper() {
            return new SubOperation();
        }
    }
    public class MulOperationFactory implements IFractory {
        @Override
        public Operation generateOper() {
            return new MulOperation();
        }
    }
    public class DivOperationFactory implements IFractory {
        @Override
        public Operation generateOper() {
            return new DivOperation();
        }
    }
    //客戶端代碼
    IFractory fractory = new AddOperationFactory();
    Operation operation = fractory.generateOper();
    operation.getResult(firstNum,secondNum);

抽象工廠

        public interface IFacfory {
            public IUser createUser();
            public IDepartment createDepartment();
        }
        public interface IUser {
            public void insert();
            public void getById();
        }
        public interface IDepartment {
            public void insert();
            public void getDepartmentById();
        }
        public class SqlServerUser implements IUser {
            @Override
            public void insert() {
                System.out.println("insert into sqlserver.");
            }

            @Override
            public void getById() {
                System.out.println("get user by id from sqlserver.");
            }
        }
        public class SqlServerDepartment implements IDepartment {
            @Override
            public void insert() {
                System.out.println("insert department into sqlserver.");
            }

            @Override
            public void getDepartmentById() {
                System.out.println("get department in sqlserver by id.");
            }
        }

        public class AccessUser implements IUser {
            @Override
            public void insert() {
                System.out.println("insert into access");
            }

            @Override
            public void getById() {
                System.out.println("get by id from access");
            }
        }
        public class AccessDepartment implements IDepartment {
            @Override
            public void insert() {
                System.out.println("insert department into sqlserver.");
            }

            @Override
            public void getDepartmentById() {
                System.out.println("get department in sqlserver by id.");
            }
        }

        //不同產品組使用一個工廠
        public class SqlServerFactory implements IFacfory {
            @Override
            public IUser createUser() {
                return new SqlServerUser();
            }

            @Override
            public IDepartment createDepartment() {
                return new SqlServerDepartment();
            }
        }
        public class AccessFactory implements IFacfory {
            @Override
            public IUser createUser() {
                return new AccessUser();
            }

            @Override
            public IDepartment createDepartment() {
                return new AccessDepartment();
            }
        }
        客戶端:
        IFacfory facfory = new AccessFactory();
        IUser user = facfory.createUser();
        IDepartment department = facfory.createDepartment();
        user.insert();
        user.getById();
        department.insert();
        department.getDepartmentById();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章