Overview

  • Dart에서 JSON을 parsing하는 class를 만들 때 fromJson이라는 factory named constructor를 만들곤 한다.

    class Person {
      final String name;
      final int age;
    
      Person({required this.name, required this.age});
    
      // ✅
      factory Person.fromJson(Map<String, dynamic> json) =>
          Person(name: json["name"], age: json["age"]);
    }
    
  • 그런데, fromJson constructor는 factory가 아니어도 아무 문제가 없다.

    class Person {
      final String name;
      final int age;
    
      Person({required this.name, required this.age});
    
      // ✅
      Person.fromJson1(Map<String, dynamic> json)
          : name = json["name"],
            age = json["age"];
    }
    
  • Dart 문서의 예시에서도 fromJson을 factory constructor로 만들고 있다.

    class Logger {
      // ...
    
      factory Logger(String name) {
          return _cache.putIfAbsent(name, () => Logger._internal(name));
      }
    
      // Logger.fromJson factory constructor initializes a final variable from a JSON object.
      factory Logger.fromJson(Map<String, Object> json) {
          return Logger(json['name'].toString());
      }
    
      // ...
    }
    
  • fromJson constructor는 factory로 만들라고 하는 것일까?

Factory constructor

  • Dart 문서에서 factory constructor는 아래의 경우에 해당되면 사용하라고 설명하고 있다.
    • Constructor가 subtype instance를 생성하거나, cache 등에서 이미 존재하는 instance를 가져와서 반환하는 경우
    • Argument 검사 또는 initializer list에서 처리되지 않은 작업 등 instance 생성 과정에서 선행되어야 하는 중요한 작업을 수행해야 하는 경우
  • 함께 소개된 예시 코드에서 Logger constructor는 cache에서 객체를 가져와서 반환하므로 factory constructor를 사용하면 좋다.
  • 하지만, Logger.fromJson은 json object로부터 name data를 가져와서 Logger instance를 생성하고 있다. Json을 parsing하는 과정에서 선행되는 작업도 없고, json data를 사용해서 ‘항상’ 새 instance를 만들어서 반환하는데 왜 factory constructor로 만드는 것일까?

Defensive design

  • Stack overflow에서 관련된 질문과 답변을 찾을 수 있었다.
  • Factory constructor를 사용하는 것은 기술적으로 특별한 이유가 없더라도 defensive design을 위한 것이라고 한다.
  • 일반적인 generative constructor를 만들면 subclass에서 forwarding이 가능해 진다.
  • 이 constructor에 validation code를 추가하는 등의 변경이 필요하면 factory constructor로 변경해야 하는데, 이 class를 상속받는 subclass들 중 constructor를 forwarding 하고 있던 class에서 예상하지 못한 error가 발생할 가능성이 생긴다.
  • 따라서, fromJson 외에도 constructor는 먼저 factory로 만들고 필요할 때만 public generative constructor를 만드는게 안전하다고 한다.

Conclusion

  • Stack overflow의 답변에 따르면, factory constructor를 사용하는 이유는 subclass에서 의도하지 않은 forwarding에 의한 error 가능성을 제거하는 목적이다.
  • 하지만, 이것보다는 json 객체를 parsing하는 constructor의 역할 때문인 것 같다.
  • 간단한 json 객체는 특별히 다른 logic이 필요없어 보이지만, 일반적으로 json 객체를 parsing할 때는 실패할 가능성이 존재하므로 이와 관련된 방어 코드 등의 logic이 필요할 수 있다.
  • 따라서, json 객체로부터 instance를 생성하는 constructor는 factory constructor를 사용해야 하는 두 번째 이유에 해당한다고 생각한다.
  • 이 질문에 대한 답은 정해진 것이 없으므로, fromJson은 일반적인 구현 패턴을 따라 factory constructor로 만드는게 좋을 것 같다.

Reference