flutterdartinheritanceconstructorcloning

Dart standardizing logic of child class clone methods using existing Map methods


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())
}

Solution

  • 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.