skip to Main Content

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

2

Answers


  1. Chosen as BEST ANSWER

    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']

    abstract class ParentClass<T extends ParentClass<T>> {
      String foo;
      int bar;
      var tType; //note -- cannot be T tType;
    
      Model({
        String? foo,
        int? bar
        Map? kwargs,
        required this.tType, // As <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.


  2. To standardize the clone method for every child of your abstract class ParentClass, you can utilize a pattern called the "copy constructor" pattern. This pattern involves adding a constructor to your abstract class that takes an instance of the same class as an argument and creates a new instance based on the properties of the provided instance. Here’s how you can implement it in your code:

    class ParentClass<T> {
      String? foo;
      int? bar;
    
      ParentClass({
        this.foo,
        this.bar,
      });
    
      T clone() {
        throw UnimplementedError('Subclasses must implement clone method');
      }
    
      Map<String, dynamic> toMap() {
        return {
          'foo': foo,
          'bar': bar,
        };
      }
    }
    
    class ChildClass extends ParentClass<ChildClass> {
      ChildClass({
        String? foo,
        int? bar,
      }) : super(foo: foo, bar: bar);
    
      @override
      ChildClass clone() {
        return ChildClass(foo: foo, bar: bar);
      }
    
      // Implement additional methods or properties specific to ChildClass if needed.
    }
    
    void main() {
      // Create an instance of ChildClass
      final childInstance = ChildClass(foo: 'Hello', bar: 42);
    
      // Clone the instance
      final clonedInstance = childInstance.clone();
    
      // Convert the instance to a Map
      final map = childInstance.toMap();
    
      // Print the original instance, cloned instance, and the map representation
      print('Original: $childInstance');
      print('Cloned: $clonedInstance');
      print('Map: $map');
    }
    
    

    With this implementation, you’ve added a copy constructor to your abstract class ParentClass. This copy constructor takes an instance of the same class as an argument and initializes the new instance with the properties of the provided instance. The clone method in your abstract class now simply calls this copy constructor to create a new instance of the same type with the same properties.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search