Pull to refresh

Comments 8

Как-то я не уловил смысла в этой VariantFactory. Для разных типов нужны разные наборы по разному подготавливаемых аргументов. Тот, кто их предоставляет, может сразу и создавать вариант соответствующего им типа. Единственное применение, которое я вижу для подобной фабрики, - это обобщённая (де)сериализация. Но ваше решение для этого не применимо.

Переписал ваш код на нормальном языке:

import std.stdio, std.format, std.sumtype, std.range;

struct Point {
    int x, y;
    int area() { return 0; }
    string toString() {
        return "[%s,%s]".format( this.x, this.y );
    }
}

struct Rect {
    Point pos, size;
    int area() { return this.size.x * this.size.y; }
    string toString() {
        return "[rect:%s#%s]".format( this.pos, this.size );
    }
}

struct Line {
    Point from, to;
    int area() { return 0; }
    string toString() {
        return "[line:%s->%s]".format( this.from, this.to );
    }
}

alias Shape = SumType!( Point, Rect, Line );

void main() {
    Shape[] shapes;
    shapes ~= Shape( Point(1,2) );
    shapes ~= Shape( Rect( Point(1,2), Point(1,2) ) );
    shapes ~= Shape( Line( Point(1,2), Point(3,4) ) );
    foreach( shape; shapes ) shape.writeln;
    pragma(msg,Shape.Types);
}
(Point, Rect, Line)
[1,2]
[rect:[1,2]#[1,2]]
[line:[1,2]->[3,4]]

Считайте, что Shape - это и есть ваша фабрика.

Из изначального кода в статье можно просто удалить лишнее - конструкторы, заменить iostream на format, получится буквально тот код который вы и с кинули (только без непонятного shapes ~=)

Не совсем понятно, чем явное указания class type из enum лучше чем указание самого класса при вставка в вариант.

Возникает следующий вопрос: а как мы можем абстрагировать, отделить способ создания от самого объекта?

У меня возникает другой вопрос: зачем? В какой ситуации код

using VF = VariantFactory<Point, Line, Rect>;
using ShapeType = VF::ResultType;
 ShapeType shape;
 VF{}( &shape, kUniqueId<line>, 1, 1, 4, 5);

будет лучше, чем две строки ниже?

using ShapeType = std::variant<Point, Line, Rect>;
ShapeType shape{std::in_place_type_t<Line>, 1, 1, 4, 5};
 

Этот вопрос применим и к обычной фабрике - там тоже можно просто вызывать конструкторы.

Стандартные выгоды: вынос общего бойлерплейта (логгирование, кэширование, пул памяти) или возможность передать фабрику как внешний параметр для другого шаблона вроде аллокатора из STL.

Клиентский код не всегда может вызывать конструкторы -- например, мы можем хотеть иметь возможность подменять тип создаваемого объекта или клиентский код может не иметь значений параметров. В данном же случае фабрике из клиента передаётся и тип, и параметры. Точнее, ID типа, но он взаимно однозначно соответствует типу.

Для выноса бойлерплейта достаточно шаблонной функции в три строчки, а в случае передачи этой фабрики извне я опять же не понимаю зачем.

Следите за руками:

Shape shape( Type, Args ... )( Args args ) {
    "create %s%s".format( Type.stringof, [args] ).writeln;
    return Type( args ).Shape;
}
shapes ~= Point(1,2).Shape; // no log
shapes ~= shape!Point( 1, 2 ); // create Point[1, 2]

Неужели не хочется писать простой и лаконичный код?

Sign up to leave a comment.