Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart Patterns to replace static inheritance

In Dart, static methods aren't inherited. I come primarily from a PHP background, where static method inheritance is not only possible, but made extremely easy thanks to the self and static keywords. In writing Dart applications, I keep finding myself running into situation after situation where my first thought is "I need to add a static method to a class that these other classes can then inherit."

One example would be a set of classes where I want to be able to call Foo.getDataById(id) to create a Foo object from data stored in a database under the id id. Because each of my classes would store themselves in the database in the same way, I feel that this functionality should be inherited so that I don't have to copy and paste the implementation in every class I store in the database. At the same time, this is a method that does not depend on a specific instance of the class (and indeed one doesn't have to stretch too far to think of situations where you'd want to grab the data without actually instantiating the class), which is begging for it to be a static method. Here is where I hit the Dart wall, being unable to inherit a static method, and I'm not sure what to do.

What are some other design patterns that would allow the same end result as static inheritance (namely being able to reuse the code in multiple classes and not having to create a new instance of the class when one is not strictly needed) that Dart will support? Is there an idiomatic Dart way to do this?

EDIT: While I used something that is intended to construct an instance in my example above, not all of the situations I have encountered relate to construction. That said, for those that do, I played around with factory constructors for a while, but I haven't been able to get them to work in the situations I've encountered yet. One problem is that if I am doing async work, I ultimately need to return a Future for the object, but I can't do that in a constructor (afaik). Beyond that, I also have had trouble writing a factory constructor in the superclass as it would need to access static fields in the target class, but without something like PHP's static keyword, I can't do that. I could inject them into the constructor's call, but then I'm still left with having to define the static fields in each class (not so bad) but without being able to even define a common interface that they can inherit (which is bad).

EDIT 2: I've been asked for some more concrete examples of what I'm trying to accomplish. Suppose I am making a game in which different type of upgrades can be purchased, each upgrade being purchasable multiple times. Here I would make each upgrade type its own class, with some common interface to define methods like isActive() or apply() or whatever. along with properties like cost. Now, suppose I want each upgrade to have a cost that is a function of a base cost for that type and the number of active upgrades of that type already owned (so they get more expensive as you purchase them). The way I would want to do this is by adding a getCost(numberOfPurchasedUpgrades) static method to a superclass, so that I can define the standard function that determines the cost of an upgrade. This static method could be overridden by specific upgrade type if that upgrade didn't follow the default price progression. This methed also only makes sense in static context, as I would often need to be able to get the price of a type of upgrade without actually needing an instance of that upgrade type. Without static inheritance, however, I would have to either make the getCost method non-static, which would mean it would need to be invoked by new UpgradeType1().getCost(countOfUpgradeType1), or I would need to define the same static getCost method in every upgrade class. The former creates instances that aren't needed and just get thrown away, the latter does nothing to enforce the existence of a getCost method on an upgrade (meaning I wouldn't be able to count on it being there, which kinda ruins polymorphism) and would require me to copy and paste the same logic multiple places. I could solve the copy and paste problem by making it a top level function, but then I'm polluting the global namespace and I still have nothing to enforce the interface. Surely there's a better way to go about this.

Another example would be in creating some basic CRUD functionality on a set of objects. I'd like to define a superclass that can do the work of creating a table to display existing objects of that type, a form for creating a new object, etc. No static inheritance means that I can't do this, so my next thought was to make a table building class that gets the objects passed in. But now if I want to create a new object of that type, I have no way to reference that class. Is the solution the same, and I need to pass more into the table builder? If so, where does it end? Will the table building class need an array of items, a create function, a delete function, and an update function all passed into the constructor?

like image 502
Michael Fenwick Avatar asked Dec 22 '13 05:12

Michael Fenwick


People also ask

How do you override a static method in darts?

Inheritance of static methods has little utility in Dart. Static methods cannot be overridden. Any required static function can be obtained from its declaring library, and there is no need to bring it into scope via inheritance.

Are static methods inherited Dart?

In Dart, static methods aren't inherited.

What is valid for Dart static methods?

Dart Static Methods A static method is only allowed to access the static variables of class and can invoke only static methods of the class. Usually, utility methods are created as static methods when we want it to be used by other classes without the need of creating an instance.

Why static methods Cannot be inherited?

Static methods in Java are inherited, but can not be overridden. If you declare the same method in a subclass, you hide the superclass method instead of overriding it. Static methods are not polymorphic. At the compile time, the static method will be statically linked.


1 Answers

If I understand you correctly you want use the following pattern.

abstract class Base {
  Base getById(id);
}

class Foo extends Base {
}

And later.

var foo = Foo.getById(id);

You want to assign everything to the model?

This is a very primitive way. Static classes is not usable in many cases.

Look this sample. This sample is a very simple but it demonstrate why not need to use static classes.

import 'package:queries/queries.dart';
import 'package:reflection/reflection.dart';

// Begin user code
class MyDatabase extends Database {
  DbSet<Category> categories;

  DbSet<Product> products;
}

class Category {
  int id;

  String name;

  String toString() => name;
}

class Product {
  Category category;

  int id;

  String name;

  String toString() => name;
}

// End user code

void main() {
  var db = new MyDatabase();
  var odd = db.categories.firstOrDefault((c) => c.name == "Optical disk drives");
  if(odd != null) {
    print("======================");
    print(odd.name);
    print("----------------------");
    var products = db.products.where((p) => p.category == odd);
    for(var product in products) {
      print(product);
    }
  }

  var threeProducts = db.products.orderBy((p) => p.name).take(3);
  print("======================");
  print("Only three products");
  print("----------------------");
  for(var product in threeProducts) {
    print(product);
  }
}

// Internal code

class DbSet<T> extends Enumerable<T> {
  TypeInfo _ti;

  DbSet() {
    _ti = typeInfo(T);
  }

  Iterator<T> get iterator {
    var data = new _Data()._data[T];
    if(data != null) {
      return data.iterator;
    }

    return null;
  }
}

abstract class Database {
  Database() {
    var queryableType = typeInfo(Enumerable);
    var ti = typeInfo(runtimeType);
    var variables = ti.getVariables(BindingFlags.PUBLIC | BindingFlags.INSTANCE
      | BindingFlags.DECLARED_ONLY);
    for(VariableInfo variable in variables.values) {
      if(variable.type.isA(queryableType)) {
        var object = variable.type.newInstance().reflectee;
        variable.setValue(this, object);
      }
    }
  }
}

class _Data {
  static _Data _current;

  Map _data = {};

  factory _Data() {
    if(_current == null) {
      _current = new _Data._internal();
    }

    return _current;
  }

  _Data._internal() {
    _fillData();
  }

  void _fillData() {
    var vga = new Category();
    vga.id = 1;
    vga.name = "Video adapters";
    var odd = new Category();
    odd.id = 2;
    odd.name = "Optical disk drives";
    var categories = [vga, odd];
    var products = [];
    var geforce = new Product();
    geforce.id = 1;
    geforce.category = vga;
    geforce.name = "GeForce GTX650";
    products.add(geforce);
    var radeon = new Product();
    radeon.id = 2;
    radeon.category = vga;
    radeon.name = "Radeon HD7770";
    products.add(radeon);
    var sh224 = new Product();
    sh224.id = 3;
    sh224.category = odd;
    sh224.name = "DVD-RW Samsung SH-224";
    products.add(sh224);
    var dvr221 = new Product();
    dvr221.id = 4;
    dvr221.category = odd;
    dvr221.name = "DVD-RW Pioneer DVR-221";
    products.add(dvr221);
    _data[Category] = categories;
    _data[Product] = products;
  }
}
======================
Optical disk drives
----------------------
DVD-RW Samsung SH-224
DVD-RW Pioneer DVR-221
======================
Only three products
----------------------
DVD-RW Pioneer DVR-221
DVD-RW Samsung SH-224
GeForce GTX650

P.S.

If you want CRUD you may use database (applied) objects.

// Begin user code

class SaleOrder extends DocumentObject {
  Customer customer;
}

class Customer extends CatalogObject {
  String address;
}

class Inventory extends MovementObject {
  Product product;

  double quantity;
}

// End user code

abstract class DbObject {
  dynamic id;
}

abstract class AppliedObject extends DbObject {
  void delete();

  void save();
}

abstract class CatalogObject extends AppliedObject {
  String name;  

  void delete() {
    // delete catalog object
  }

  void save() {
    // save catalog object
  }
}

abstract class DocumentObject extends AppliedObject {
  DateTime date;

  dynamic number;

  void delete() {
    // delete document object
  }

  void save() {
    // save document object
  }
}

abstract class MovementObject extends DbObject {
  DocumentObject owner;

  void write() {
    // write movement object
  }
}
like image 111
mezoni Avatar answered Sep 30 '22 19:09

mezoni