2014-12-22 3 views
6

이제 몇 번씩 클래스를 구성하는 방법에 대한 인터페이스를 정의해야하는 유스 케이스가 생겼습니다. 객체를 직렬화하고 직렬화를 해제 할 수있는 인터페이스를 정의하는 인터페이스 클래스를 만들고 싶다면 (예를 들어, 데이터베이스에 입력하고 JSON으로 보내려면 등). 당신은 다음과 같이 작성할 수 있습니다건설 용 인터페이스 만들기

abstract class Serializable { 
    String serialize(); 
    Serializable unserialize(String serializedString); 
} 

을하지만 serialize() 제대로 인스턴스 방법으로 지금은 문제가 있고, unserialize() 대신 정적 메소드이어야한다 (상속하지 않은 또는 인터페이스에 의해 시행) 또는 생성자 (상속 받지도 않음).

이것은 Serializable 인터페이스를 impliment 클래스가 serialize() 방법을 정의하는 데 필요한 상태를 잎하지만 static unserialize() 방법 또는 Foo.fromSerializedString() 생성자를 정의하는 클래스를 필요로 할 수있는 방법은 없습니다. 오히려 복잡하고 추악한

Foo foo = new Foo(); 
foo = foo.unserialize(serializedString); 

:

당신이 인스턴스 메서드 unserialize()을하면, 다음과 같이 것 인 구현 클래스 Foo을 일렬 화를 푸는.

내가 생각할 수있는 유일한 다른 옵션은 Serializable 인터페이스에 주석을 추가하여 구현 클래스가 적절한 정적 메서드 또는 생성자를 정의하는지 확인하는 것입니다. 그러나 개발자가 코드를 놓치면 코드가 손상되고 오류가 발생하기 쉽습니다. 완성.

이렇게하려면 더 좋은 방법이 있습니까? 클래스를 구현하여 자신을 구성하는 방법을 정의하거나 일반적인 효과를주는 인터페이스를 가질 수있는 패턴이 있습니까?

+0

Foo 개체에서 FooSerializer로 직렬화를 분리하면 어떨까요? 이 FooSerializer는 두 메서드를 인스턴스 메서드로 사용합니다. –

+0

마지막 예제에서 코드는 serializedString에 Foo 객체가 있음을 이미 알고 있습니까? 일반적으로 deserialize 할 때 코드는이를 미리 알지 못합니다. 따라서 실제로는 정적 메서드 또는 deserialization을 시작한 다음 올바른 구체화 구현에 위임 할 함수가 필요합니다. 이것은 Map 을 사용하여 매우 우아하게 수행 할 수 있습니다. 또한 Dart std lib에서는 일반적으로 encode/decode라고 불리며 Java 직렬화/직렬화를 선호합니다. –

+0

저는 제가 의미하는 예를 들어 답을 추가했습니다. –

답변

3

상속을 보장하려면 인스턴스 메서드를 사용해야합니다. 리플렉션을 사용하여 수동 인스턴스화보다 조금 더 멋지게 할 수 있습니다.이제

abstract class Serializable { 

    static Serializable fromSerializedString(Type type, String serializedString) { 
    ClassMirror cm = reflectClass(type); 
    InstanceMirror im = cm.newInstance(const Symbol(''), []); 
    var obj = im.reflectee; 
    obj.unserialize(serializedString); 
    return obj; 
    } 

    String serialize(); 
    void unserialize(String serializedString); 
} 

누군가가이 때 unserialize 방법을 제공하도록 강요 될 것이다 Serializable 구현하는 경우 : 당신과 같이 인스턴스를 얻을 수

class Foo implements Serializable { 

    @override 
    String serialize() { 
    // TODO: implement serialize 
    } 

    @override 
    void unserialize(String string) { 
    // TODO: implement unserialize 
    } 
} 

:

var foo = Serializable.fromSerializedString(Foo, 'someSerializedString'); 

것은이 조금 더 예뻐 수 있습니다을 수동 방법보다 자연스럽지 만, 수반 될 수있는 모든 문제가있는 반사를 사용한다는 점을 명심하십시오.

정적 메서드와 경고 주석을 대신 사용하기로 결정한 경우 Serializable을 구현하는 모든 클래스를 검사하여 사용자에게 경고하거나 빌드가없는 경우 빌드를 중지하는 사용자 정의 Transformer을 제공하면 도움이 될 수 있습니다 해당 static unserialize 메서드 또는 생성자 (Polymer의 작업과 유사). 이것은 분명히 인스턴스 메소드로 편집기가 할 수있는 즉각적인 피드백을 제공하지 않지만, 문서의 간단한 주석보다 더 눈에. 것입니다.

+1

도움을받을 수 있다면 반성을 피하기를 희망했습니다. 언급했듯이 일단 사용하기 시작하면 많은 수하물이 들어갑니다. 변압기 아이디어는 꽤 재미있어 보이지만, 전에 많이 사용 해본 적이 없다. 아마 어떻게 작동 할 수 있는지에 대한보다 자세한 예제를 제공 할 수 있습니까? (아마도이 ​​답에 대한 의견보다는 주 질문에 대한 별도의 대답으로)? –

+0

나는 트랜스포머 제안에 대해 주로이 대답을 수락합니다. 거울도 작동 할 수는 있지만, 왜 거울을 사용하지 않을지에 대한 많은 이유가 있습니다. 트랜스포머는 엄청난 양의 오버 헤드를 추가하지 않고도 코드가 올바르다는 것을 확인하는 가장 간단한 솔루션입니다. –

+0

죄송합니다. 다시 돌아와서 변압기 부품을 확장하는 걸 잊었습니다. 어쨌든 당신이 알아 낸 것 같아요. –

0

나는 당신과 같이 이름을 생성자로 때 unserialize 함수를 정의 제안 :

abstract class Serializable<T> { 
    String serialize(); 
    Serializable.unserialize(String serializedString); 
} 

이 정적 방법의 필요가 없습니다. 가능한 구현은 다음과 같이 수 :

import 'dart:convert'; 
class JsonMap implements Serializable<JsonMap> { 
    Map map = {}; 

    JsonMap() { 
    } 

    String serialize() { 
     return JSON.encode(map); 
    } 

    JsonMap.unserialize(String serializedString) { 
     this.map = JSON.decode(serializedString); 
    } 
} 

당신은 (비)과 같이 직렬화 할 수 있습니다

JsonMap m = new JsonMap(); 
m.map = { 'test': 1 }; 
print(m.serialize()); 

JsonMap n = new JsonMap.unserialize('{"hello": 1}'); 
print(n.map); 

을이를 테스트하는 동안, 당신이 해달라고하면 다트 당신 오류가 발생하지 않을 것으로 나타났습니다 실제로 클래스가 구현하기로 약속하는 메소드를 implements으로 구현하십시오. 이건 내 로컬 다트와의 격렬한 관계 일 수도 있습니다.

+1

마지막 단락은 피하려고하는 정확한 상황입니다. –

+0

알 겠지만 추상 클래스는 이것을 수행하는 Dart 방법입니다. Dart 클래스의 핵심 클래스는 Comparable 또는 Iterable 같은 인터페이스에 이것을 사용합니다. 구현이 불완전한 경우 Dart가 경고 또는 오류를 적용하지 않는 이유는 알지 못합니다. –

+1

Comparable 또는 Iterable은 구현이 다르므로 생성자가 올바르게 작동하도록 생성자를 정의 할 필요가 없으므로 서로 다릅니다. 생성자는 클래스 인터페이스의 일부가 아니므로 편집기는 경고를 표시하지 않습니다. –

0

가능한 패턴은 다소 덜 어색한 방법으로 인스턴스 메서드를 사용하는 팩토리 클래스를 만드는 것입니다. 같은 뭔가 다음 : 마지막으로

typedef Constructable ConstructorFunction(); 

abstract class Constructable { 
    ConstructorFunction constructor; 
} 

abstract class Serializable { 
    String serialize(); 
    Serializable unserialize(String serializedString); 
} 

abstract class SerializableModel implements Serializable, Constructable { 
} 

abstract class ModelFactory extends Model {  
    factory ModelFactory(ConstructorFunction constructor) { 
    return constructor(); 
    } 

    factory ModelFactory.fromSerializedString(ConstructorFunction constructor, String serializedString) { 
    Serializable object = constructor(); 
    return object.unserialize(serializedString); 
    } 
} 

및 구체적인 구현 : SerializableModel

Foo foo = new ModelFactory.fromSerializedString(Foo.constructor, serializedString); 

모두의 결과로 구성 될 수있다 확장 지금

class Foo extends SerializableModel { 
    //required by Constructable interface 
    ConstructorFunction constructor =() => new Foo(); 

    //required by Serializable interface 
    String serialize() => "I'm a serialized string!"; 
    Foo unserialize(String serializedString) { 
    Foo foo = new Foo(); 
    //do unserialization work here to populate foo 
    return foo; 
    }; 
} 

Foo (또는 아무것도 이것은 모든 구체적인 클래스가 새로운 클래스를 생성 할 수있는 메소드를 가지고 있다는 것을 강요합니다. 직렬화 된 캐릭터 라인으로부터 그 자신의 인스턴스를 취득 해, 정적 인 문맥으로부터 그 메소드를 호출 할 수 있도록하는 공통의 인터페이스가 존재합니다. 정적 객체에서 인스턴스 객체로 전환하는 것을 전적으로 목적으로하고 추가 된 객체를 생성하는 중입니다. 그리고 다른 많은 오버 헤드가 있지만, 적어도 모든 우스꽝 스러움은 사용자에게 숨겨져 있습니다. 아직도, 나는 이것을 달성하는 것이 최선의 방법이라는 것을 아직 확신하지는 못했다.

1

이 예제는 인코딩 및 디코딩을 구현하는 데있어 다트와 같은 방법이라고 생각합니다. 실제로 나는 디코드 시그니처를 "시행"하는 것이 실제로 버그를 잡거나 코드 품질을 향상시키는 데는 도움이되지 않는다고 생각합니다. 디코더 유형을 플러그 가능으로 설정해야하는 경우 디코더 매핑을 구성 가능하게 만들 수 있습니다.

const Map<String,Function> _decoders = const { 
    'foo': Foo.decode, 
    'bar': Bar.decode 
}; 

Object decode(String s) { 
    var obj = JSON.decode(s); 
    var decoder = _decoders[obj['type']]; 
    return decoder(s); 
} 

abstract class Encodable { 
    abstract String encode(); 
} 

class Foo implements Encodable { 
    encode() { .. } 
    static Foo decode(String s) { .. } 
} 

class Bar implements Encodable { 
    encode() { .. } 
    static Foo decode(String s) { .. } 
} 

main() { 
    var foo = decode('{"type": "foo", "i": 42}'); 
    var bar = decode('{"type": "bar", "k": 43}'); 
}