web analytics

Dart : รู้จักกับ factory constructor ในภาษา Dart

สวัสดีผู้อ่านทุกท่านครับ วันนี้มาเสริมความรู้กันในภาษา dart เบาๆ เมื่อเราลองเขียนภาษา dart มาสักพัก ก็น่าจะเจอ keyword ว่า factory และจะพบบ่อยมากเมื่อได้เขียนโปรแกรมถึงส่วน api หรือ json ทั้งหลาย มันคืออะไรกันนะ

Factory constructor

factory เป็น constructor แบบหนึ่งที่ไม่จำเป็นต้องสร้าง instance ใหม่เสมอ กล่าวคือ constructor โดยปกติ เวลาเราเรียกใช้ สิ่งที่ได้มาคือ instance ที่เพิ่งถูกสร้างใหม่สดๆร้อนๆทุกครั้งไม่มียกเว้น แต่เมื่อเรากำหนด factory ให้มัน มันจะเป็น constructor ที่อินดี้ไม่เหมือนเดิม ที่จะสร้าง instance ใหม่ก็ได้ หรือไม่ก็ได้ หรือจะ return null ก็ทำได้ อยู่ที่เรากำหนด ซึ่งโดยทั่วไปก็มักจะเอาไว้ทำ cache ของ instance

ดู code ตัวอย่าง class Universe ซึ่งมีค่า static ตัวนึง เอาไว้เป็น cache แล้วก็ กำหนด factory constructor เอาไว้ เมื่อไหร่ที่เราสร้าง Universe instance เราจะได้ universe ตัวเดิมเสมอ

class Universe{
  
  String name;
  
  static Universe u = Universe.init(); 
  
  factory Universe(){
    return u;
  }
  
  Universe.init(){
    name = "A";
  }
}

  
void main() {
  Universe u1 = Universe(); 
  Universe u2 = Universe();
  
  u1.name = "B";
  
  print(u1.name);  // "B"
  print(u2.name);  // "B"
}

ลอง code รันได้ที่ Dartpad โดยคลอกที่ลิงค์ข้างล่างนี้เลย
https://dartpad.dartlang.org/14517b3eb0d72f8da917448368b39e22

สังเกตว่า ไม่ว่าจะสร้าง Universe 1 หรือ Universe 2 ก็จะได้ตัวเดิม เพราะมัน return cache เป็นตัวแปร static นั่นเอง

มาดูอีกตัวอย่างที่มักจะเจอกันบ่อยๆ คือการกำหนดชื่อให้ factory เช่น fromJson โดยหน้าที่ของมันคือการสร้าง instance (หรือไม่ก็ได้) จาก json นั่นเอง

import 'dart:convert';

class Universe{
  
  String name;
  
  Universe();

  factory Universe.fromJson(Map<String,dynamic> map){
    Universe u = Universe()
    ..name = map["name"];
    return u;
  }
}

void main() {
  final jsonA = json.decode("{\"name\":\"A\"}");
  final jsonB = json.decode("{\"name\":\"B\"}");
  
  Universe u1 = Universe.fromJson(jsonA);
  Universe u2 = Universe.fromJson(jsonB);
  
  print(u1.name);  // "A"
  print(u2.name);  // "B"
}

https://dartpad.dartlang.org/afb0417bca3d9d640d341d7b3f56427c

Factory vs Static method

เมื่อ factory constructor สามารถตั้งชื่อได้ คำถามคือ มันต่างอะไรกับ static method ที่มันไม่ต้องใช้ keyword factory ก็ได้ ใช้ static method ก็สามารถทำงานได้เหมือนกัน เช่น fromJson แบบ static method

  ..
  static Universe fromJson(Map<String,dynamic> map){
    Universe u = Universe()
    ..name = map["name"];
    return u;
  }
}

void main() {
  final jsonA = json.decode("{\"name\":\"A\"}");
  final jsonB = json.decode("{\"name\":\"B\"}");
  
  Universe u1 = Universe.fromJson(jsonA);
  Universe u2 = Universe.fromJson(jsonB);
  
  print(u1.name);  // "A"
  print(u2.name);  // "B"
}

ผลลัพธ์ทั้งคู่เหมือนกัน ตอนเรียกใช้ยังสามารถเขียนเหมือนกันได้อีก คำตอบคือ มันอาจจะสามารถทำงานได้เหมือนกันในหลายๆกรณี แต่โดยส่วนใหญ่คอนเซ็ปต่างกัน ต่างตรงที่ factory เป็น constructor ดังนั้นมันจะ return ได้แค่ instance ของ class ตัวเองหรือ subclass เท่านั้น ซึ่ง static method จะเป็น class อะไรก็ได้ และเมื่อตัว factory มันเป็น cosntructor มันสามารถ ใช้ new นำหน้าได้ (dart2 สามารถเขียนแบบไม่มี new ได้) ในขณะที่ static method จะใช้แบบ method ทั่วไป ใช้ new keyword ไม่ได้

Universe u1 = new Universe.fromJson(jsonA);
Universe u2 = new Universe.fromJson(jsonB);
// static method cannot use new keyword.

อีกกรณีนึงคือ static method สามารถใส่ async เพื่อ return Future ได้ แต่ factory จะทำไม่ได้ เพราะ factory จะ return ได้แค่ class ของ ตัวเองหรือ subclass เท่านั้น ทำให้ใช้ async ไม่ได้นั่นเอง

static Future<Universe> fromJson(Map map) async {
     // Factory constructor cannot do this.
}

ดังนั้นมาถึงตรงนี้ ผู้อ่านน่าจะสามารถตอบได้แล้วว่า ทำไม fromJson ถึงต้องเป็น factory constructor นั่นก็เพราะว่าการทำงานของ fromJson จะแปลง json เป็น object ที่จะต้อง return instance ของ class เสมอและในหลายกรณีเราอาจต้อง cache instance เอาไว้ ทำให้การใช้ factory constructor จะเหมาะกว่าการใช้ static method นั่นเอง

สรุป

factory เป็น keyword สำหรับ constructor ที่ไม่จำเป็นต้องสร้าง instance ใหม่เสมอ อยู่ที่เรากำหนด โดยทั่วไปใช้ทำ caching ให้กับ class และเราสามารถกำหนดชื่อให้กับ constructor ได้ด้วย เมื่อ factory สามารถกำหนดชื่อได้ การใช้งานจะคล้ายกับ static method มาก แต่ก็มีหลายอย่างที่ต่างกัน คอนเซ็ปก็ต่างกัน หากเราเข้าใจว่า factory ต่างจาก static method อย่างไร เราก็จะสามารถใช้

บทความอื่นๆเกี่ยวกับ Dart