I am trying to standardize the logic of a class.clone method so that every child of my abstract class clones the exact same way.
I've tried using factory constructors and all sorts of roundabouts but I can't seem to find a solution that doesn't require adding an additional function specifically for cloning, which I feel is redundant and opens me up to unwanted variance in how cloning works down the line.
The class structure is as follows:
abstract class ParentClass<T extends ParentClass<T>> {
String foo;
int bar;
Model({
String? foo,
int? bar
Map? kwargs,
}) : foo = foo ?? kwargs?['foo'] ?? "Not provided",
bar = bar ?? kwargs?['bar'] ?? 0;
Map<String, dynamic> toMap();
// toMap always returns approximately {'foo':foo1, 'bar':bar1, "x":x1, etc.}
T clone();
//Optimally along the lines of T clone() => T(kwargs:toMap())
}
Thanks ApaxPhoenix for pointing me in the right direction.
The copy pattern seems like a good practice generally but in my case there are properties of the child class which also need to be cloned by the same method. What I ended up doing is changing every child class to pass its own default constructor to itself since I know they all must follow the same system of x = x ?? kwargs['x']
for every relevant property
typedef ModelFactory<T> = T Function({Map<String, dynamic>? kwargs});
abstract class ParentClass<T extends ParentClass<T>> {
String foo;
int bar;
final ModelFactory<T> tType;
Model({
String? foo,
int? bar
Map? kwargs,
required this.tType, // As ModelFactory<T> in child constructor
}) : foo = foo ?? kwargs?['foo'] ?? "Not provided",
bar = bar ?? kwargs?['bar'] ?? 0;
Map<String, dynamic> toMap();
// toMap always returns approximately {'foo':foo1, 'bar':bar1, "x":x1, etc.}
T clone() => tType(kwargs: toMap()); //new clone
}
class childClass extends ParentClass<childClass> {
double x; // New child-specific property
childClass({
String? foo,
int? bar
Map? kwargs,
double? x,
}) : x = x ?? kwargs['x'] ?? 0.0,
super(foo: foo ?? kwargs?['foo'] ?? "Not provided",
bar: bar ?? kwargs?['bar'] ?? 0
tType: () => childClass() // Passes own constructor for use in clone
@override
Map<String, dynamic> toMap() => {
"foo":foo,
"bar":bar,
"x":x
};
}
This allows childClass.clone
to automatically return childClass(kwargs: toMap)
no matter what childClass, as long as its specified again in the constructor. Still requires extra work but ensures no variance in how cloning operates.
Seems a little dirty to me so open for suggestions to optimize.