แทนที่ Constructor ด้วย Static Factory Method


ใน Java นั้นถึงแม้ปกติแล้วเราจะสามารถสร้าง Public Constructor ใน Class ของเราได้ แต่เราก็ยังสามารถใช้วิธีอื่นให้สามารถทำงานได้คล้าย Constructor เพื่อความยึดหยุ่นของการใช้งาน อย่างการทำ Static Factory Method ตัวอย่างสำหรับคนที่เคยเขียน Java ที่พบกันบ่อยๆ ก็คือ Boolean ใน Boolean จะมี Static Factory Method สำหรับการแปลงค่า primitive type เป็น reference type

public final class Boolean implements java.io.Serializable, Comparable<Boolean> {
....
    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
...
}

เมื่อเราใช้งาน Static Factory Method จะมีรูปแบบดังนี้

Boolean boolean = Boolean.valueOf(true);

เมื่อเรารู้วิธีการสร้างและการใช้งานแล้วเรามาดูข้อดีและข้อเสียของการใช้งาน Static Factory Method กัน

ข้อดีข้อแรก Static Factory Method มีชื่อสำหรับการใช้งานแต่ Constructor ไม่มี
การที่เราสามารถตั้งชื่อให้ Static Factory Method เรานั้นทำให้คนที่อ่านโค๊ดเข้าใจการทำงานของโค๊ดง่ายขึ้นตัวอย่าง

Boolean boolean1 = new Boolean(true);
Boolean boolean2 = Boolean.valueOf(true);

จากข้างบนจะเห็นได้ว่าเราใส่ค่า true ลงไปใน Constructor แต่เราไม่รู้เลยว่าใส่ไปเพื่ออะไรถ้าเราไม่อ่าน API แต่บรรทัดที่สองชื่อ method valueOf จะอธิบายว่าเราใส่ค่า true ไปเพื่ออะไรทำให้เราอ่านโค๊ดแล้วเข้าใจง่ายขึ้น และข้อสำคัญอีกอย่างเราไม่สามารถสร้าง Constructor ที่มี Signature เหมือนกันได้ การใช้ Static Factory Method จะสามารถช่วยแก้ปัญหาในเรื่องนี้ได้

ข้อดีข้อที่สอง Static Factory Method ไม่จำเป็นต้องสร้าง Object ขึ้นมาใหม่ทุกครั้งที่มีการเรียกใช้งาน
ในข้อนี้รวมถึงการทำงานที่เป็น immutable class (immutable class คือคลาสที่ต้องสร้าง Object ใหม่ทุกครั้งที่มีการเปลี่ยนแปลง เช่น String) โดยใช้เป็น preconstructed instance หรือทำการ cache instance เอาไว้สำหรับทุกๆครั้งที่มีการเรียกใช้งาน เพื่อหลีกเลี่ยงการสร้าง Object ที่ไม่จำเป็น เช่น Boolean.valueOf(true) method แบบนี้จะไม่สร้าง Object Boolean ขึ้นมาใหม่แต่จะชี้ไปยัง Object ที่เคยมีอยู่แล้ว คลาสที่ทำหน้าที่แบบนี้เราจะเรียกว่า instance-controlled ซึ่งคลาสที่ทำงานในลักษณะจะสามารถใช้ == แทน equals ได้

ข้อดีข้อที่สาม Static Factory Method สามารถ return subtype ของ return type นั้นได้
ในส่วนนี้เราต้องออกแรงกันนิดหนึ่ง เนื่องจากการอธิบายค่อนข้างซับซ้อน จึงของอธิบายด้วย service provider framework pattern ที่เป็นการแสดงการใช้ service access API ซึ่งจะประกอบด้วยสามส่วนคือ
1. service
2. provider
3. register

ส่วนของ Service

public interface Service {
    
}

ส่วนของ Provider

public interface Provider {
    Service newService();
}

ส่วนของ Register

public class Services {

    public Services() {
    }
    
    private static final Map<String,Provider> providers = new ConcurrentHashMap<>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";
    
    //Provider Registered API
    public static void registerProvider(String name,Provider p){
        providers.put(name, p);
    }
    
    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }
    
    //Service Access 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 register");
        }
        return p.newService();
    }
}

เรามาลองเทสการใช้งานดูครับ

public class TestService implements Service{
    public void getText(){
        System.out.println("TestService");
    }
}
public class TestProvider implements Provider{

    @Override
    public Service newService() {
        return new TestService();
    }
    
}
public class ServiceProvider {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String test = "testProvider";
        Provider p = new TestProvider();
        
        Services.registerProvider(test, p);
        TestService testService = (TestService) Services.newInstance(test);
        testService.getText();
    }
}

เห็นไหมครับว่า API ที่ใช้สามารถ return subtype(TestService) ของ return type(Service) ได้

ข้อดีข้อสี่ Static Factory Method ลดการระบุ type
ตัวอย่างเลยละกันครับ คือต้องการสร้าง HashMap ที่มี key เป็น String มี value เป็น List ปกติเราจะทำการสร้างแบบนี้

Map<String,List<String>> m = new HashMap<String,List<String>>();

เราสามารถนำมาสร้างเป็น static factory method ได้โดยเขียนแบบนี้

public static <K,V> HashMap<K,V> newInstance(){
    return new HashMap<K,V>();
}

เมื่อเราต้องการสร้าง HashMap เราสามารถใช้

Map<String,List<String>> m = HashMap.newInstance();

ข้อเสียข้อหนึ่ง Static Factory Method ถ้ามีเพียง static factory method ไม่มี public หรือ protected constructor จะไม่สามารถทำ subclass ได้
ข้อนี้เป็นไปตามกฎการสืบทอดทั่วไปครับ แต่ก็จะมีบางคลาสที่ไม่ต้องการให้มี subclass เช่นกัน

ข้อเสียข้อสอง Static Factory Method นั้นยากที่แยกแยะว่าตัวไหนเป็น static method ธรรมดาหรือ static factory method
โดยทั่วไปใน static factory method มักจะใช้ชื่อ method valueOf,of,getInstance,newInstance,getType,newType

Ref. Prentice Hall – Effective Java (P.5-10)



me on google plus+Jirawong Wongdokpuang

Advertisements

One thought on “แทนที่ Constructor ด้วย Static Factory Method

  1. Pingback: Static Factory Method | Geniustree Blogs

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s