分類
Anti-Pattern

Constance Interface Pattern(常數介面模式)

Constance Interface Pattern(CIP)(常數介面模式)
這是個 Anti-Pattern,應該使用其他方法取代它。
CIP 的定義如下
一個介面,該介面沒有任何方法,只有 final static 的常數。e.g.,

public interface ConstanceInterface {
    public static final int CONSTANCE_INT_ZERO = 0;
    public static final int CONSTANCE_INT_ONE = 1;
    public static final int CONSTANCE_INT_TWO = 2;
}

 
想使用這些常數的類別可以藉由實作該介面來取得這些常數,e.g.,

public class ConstanceInterfaceImpl implements ConstanceInterface{
    public int getConstanceInterfaceZero(){
        return CONSTANCE_INT_ZERO;
    }
}

 
CIP的缺點為若常數只提供給某些類別使用,那麼應該定義在該類別中,不需提供公共的介面。
反之,若常數是提供給多數類別使用,那麼可以改用 Utility Class 或是 enum。
Utility Class

public final class ConstanceUtil {
    public static final int CONSTANCE_INT_ZERO = 0;
    public static final int CONSTANCE_INT_ONE = 1;
    public static final int CONSTANCE_INT_TWO = 2;
    private ConstanceUtil(){
        throw new AssertionError();
    }
}

 
Enum

public enum ConstanceEnum {
    CONSTANCE_INT_ZERO(0), CONSTANCE_INT_ONE(1), CONSTANCE_INT_TWO(2);
    private final int mNumber;
    private ConstanceEnum(int number) {
        mNumber = number;
    }
    public int getNumber() {
        return mNumber;
    }
}

 
介面應該回歸於最原始的使用需求,定義型態。
除了定義型態以外不適合用於其他用途。
 
 

分類
Anti-Pattern

Telescoping Constructor Pattern (伸縮建構式)

Telescoping Constructor Pattern (伸縮建構式)
首先這是個反模式(Anti-Pattern),在參數大於等於4個的情況下考慮使用 Builder Pattern 來替換。
伸縮建構式出現在具有多個參數建構式的類別中,如

public class Account {
    private String mName;
    private String mAge;
    private String mAddress;
    private String mTelephoneNumber;
    public Account(String name) {
        this(name, "unknow");
    }
    public Account(String name, String age) {
        this(name, age, "unknow");
    }
    public Account(String name, String age, String address) {
        this(name, age, address, "unknow");
    }
    public Account(String name, String age, String address,
            String telephoneNumber) {
        mName = name;
        mAge = age;
        mAddress = address;
        mTelephoneNumber = telephoneNumber;
    }
    public void showInfo() {
        System.out.println("Name:" + mName + " Age:" + mAge + " Address:"
                + mAddress + " TelephoneNumber:" + mTelephoneNumber);
    }
}

 
客戶端可以依照需要呼叫不同的建構式,如

        Account foxx = new Account("Foxx");
        foxx.showInfo();
        Account peter = new Account("Peter", "23");
        peter.showInfo();

Console

Name:Foxx Age:unknow Address:unknow TelephoneNumber:unknow
Name:Peter Age:23 Address:unknow TelephoneNumber:unknow

 
該模式有幾個缺點,
1.客戶端難以了解不同參數以及不同建構式代表的意義。
2.客戶端有可能搞混參數順序而傳錯參數。
 
可以考慮使用 Builder Pattern 來替換伸縮建構式。

public class Account {
    private String mName;
    private String mAge;
    private String mAddress;
    private String mTelephoneNumber;
    public static class AccountBuilder {
        private String mName;
        private String mAge;
        private String mAddress;
        private String mTelephoneNumber;
        public AccountBuilder(String name) {
            mName = name;
        }
        public AccountBuilder age(String age) {
            mAge = age;
            return this;
        }
        public AccountBuilder address(String address) {
            mAddress = address;
            return this;
        }
        public AccountBuilder telephoneNumber(String telephoneNumber) {
            mTelephoneNumber = telephoneNumber;
            return this;
        }
        public Account build() {
            return new Account(this);
        }
    }
    private Account(AccountBuilder builder) {
        mName = builder.mName;
        mAge = builder.mAge;
        mAddress = builder.mAddress;
        mTelephoneNumber = builder.mTelephoneNumber;
    }
    public void showInfo() {
        System.out.println("Name:" + mName + " Age:" + mAge + " Address:"
                + mAddress + " TelephoneNumber:" + mTelephoneNumber);
    }
}

其中 AccountBuilder 即是 Builder Pattern 的主角,使用其建構式(第14行)來規定必須要傳入的參數。
接著建立其他方法(age() , address(), telephoneNumber())來設定屬性,這些方法因為具有明確的名稱可以讓客戶端容易了解方法的意圖。
最後藉由 build 方法回傳 Account 實體。
 
客戶端呼叫

Account foxx = new Account.AccountBuilder("Foxx").age("unknow").address("unknow").telephoneNumber("unknow").build();
foxx.showInfo();

Console

Name:Foxx Age:unknow Address:unknow TelephoneNumber:unknow

 
Note
為了確保各個屬性的正確,可以在build方法加入檢查機制。

public static AccountBuilder{
        ...
        public Account build() {
            if(mAge == null){
                throw new IllegalArgumentException();
            }
            return new Account(this);
        }
        ...
}

 
該檢查機制會在客戶端沒有呼叫age()方法而產生Account實體丟出Exception。

Account peter = new Account.AccountBuilder("Peter").build();

Console

Exception in thread "main" java.lang.IllegalArgumentException

 
缺點:
1.必須先建立 builder 角色,效能可能會受影響。
2.Builder Pattern 的內容比 telescoping constructor 還長,因此適用於4個以上的參數。