I was trying to write something with the following design pattern:
void main()
{
document.registerElement('abs-test', Test);
TestExtend test = new TestExtend();
document.body.append(test);
}
abstract class Test extends HtmlElement
{
Test.created() : super.created();
factory Test() => new Element.tag('abs-test')..text = "test";
}
class RedTest extends Test
{
TestExtend() : super() => style.color = 'red';
}
My aim with this is to create a custom HtmlElement registered to the abstract class "Test". This abstract class "Test" would have some properties that all elements of type Test need to have. In this example, all elements of type Test need to have the word "test" as their text.
However, I wanted then to only allow the user to create subclasses of "Test" with more specific properties. In this example we have RedTest which then sets the color of Test to red. The resulting HTML would be:
<abs-test style="color:red;">test</abs-test>
I have 2 problems with this:
1) Is it possible to call the factory constructor of a parent class? (If not, is it possible to extend HtmlElement in a different way that doesn't require a factory constructor)
2) Is it possible to extends HtmlElement with an abstract class?
I have been testing for a while with different constructors but am unable to make it work. Can anyone advice?
You register a tag with a class so that the browser can instantiate it for you. If this isn't an 1:1 relation, there is no way for the browser to know what class to instantiate.
Therefore
You need
a different tag for each class
register each tag with a concrete (non-abstract) class
You normally can call the constructor of a subclass in a factory constructor of an abstract class
factory Test() => new RedTest()..text = "test";
is valid in Dart, but because extending an element requires to return
new Element.tag('abs-test')
this won't work, because you can't return both at the same time.
You need to register each concrete subclass with a different tag like
document.registerElement('abs-test-red', TestRed);
document.registerElement('abs-test-blue', TestBlue);
Related
For abstract classes is there difference between implements and extends? Which one should I use? In Java for interfaces you would use implements, but I see dart doesn't have interfaces and both implements/extends work. If I want to declare abstract class for my api methods, should I use implements or extends?
void main() {
User user = new User();
user.printName();
}
abstract class Profile {
String printName();
}
class User extends Profile {
#override
String printName() {
print("I work!!");
return "Test";
}
}
All classes in Dart can be used as interfaces. A class being abstract means you cannot make an instance of the class since some of its members might not be implemented.
extends means you take whatever a class already have of code and you are then building a class on top of this. So if you don't override a method, you get the method from the class you extends from. You can only extend from one class.
implements means you want to just take the interface of class but come with your own implementation of all members. So your class ends up being compatible with another class but does not come with any of the other class's implementation. You can implement multiple classes.
A third options, which you did not mention, is mixin which allow us to take the implementation of multiple mixin defined classes and put them into our own class. You can read more about them here: https://dart.dev/guides/language/language-tour#adding-features-to-a-class-mixins
I have an abstract class with a generic type. I want this class to define a factory so if an implementing class doesn't have this defined, it gives a warning. However I can't figure out how to do this when using a generic type. The abstract class just keep giving me errors.
Here's my class with some examples of non-working solutions:
abstract class Bar<T> {
// Error: The class 'Bar' doesn't have a default constructor
factory Bar.someFn(Map<String, dynamic> myMap) => Bar<T>(myMap);
// Error 1: The name of a factory constructor must be the same as the name of the immediately enclosing class
// Error 2: 'T' isn't a function.
// Error 3: Try correcting the name to match an existing function, or define a method or function named 'T'
factory T.someFn(Map<String, dynamic> myMap) => T(myMap);
}
Having a correct abstract class would yield the following:
// BAD!
class Foo implements Bar<Foo> {
// Missing implementation of someFn
}
// OK!
class Foo implements Bar<Foo> {
#override
factory Foo.someFn(Map<String, dynamic> myMap) => Foo(x: myMap['x'], y: myMap['y']);
}
Is there a way to achieve what I want?
No.
Constructors are not inherited, they are not part of any interface, so nothing you write in the superclass will affect the constructors of the subclass.
Each class can define its own constructors. The only thing you can do is to add a generative constructor to the superclass, then subclasses which extend the superclass (not just implement its interface), will have to call that superclass constructor as their super-constructor invocation.
I want to declare, but not define a factory constructor in an abstract class.
In my case, I want to create a method that accepts any class that implements a String toJson() method as well as a fromJson(Map<String, dynamic> data) factory constructor.
Is there any way to achieve that in Dart?
I'm looking for something like the following, which is not valid Dart code:
abstract class JsonSerializable {
factory fromJson(Map<String, dynamic> data);
String toJson();
}
I'm afraid that it doesn't work the way you want it to.
Constructors are not part of an interface. They act more like static members.
So, you can't add a factory to the interface, and code wouldn't have any way to call the factory constructor given a type variable extending this type anyway.
So, since constructors cannot be part of interfaces, constructors also cannot be abstract. Being abstract simply means "make the member part of the interface, but no implementation is added to class".
You can declare the factory as a normal method, but then you would only be able to call it when you already have an instance, which likely isn't what you want with a constructor.
The only way to pass code around is as functions or objects with methods. So, if you want to parameterize something by a type which is JsonSerializable, and you want to be able to create such an object, you need to pass a factory function along:
T deserialize<T extends JsonSerializable>(
String json,
T factory(Map<String, dynamic> data),
) {
return factory(jsonDecode(json) as Map<String, dynamic>);
}
You an then call it with:
var myValue = deserialize(jsonString, (x) => MyClass.fromJson(x));
(If MyClass.fromJson had been a static function instead of a constructor, you could just write deserialize(jsonString, MyClass.fromJson), but Dart doesn't yet have constructor tear-offs).
As suggested in the accepted answer, I ended up creating a Serializer<T> type that got implemented by a serializer for each class:
Turns out, this has several benefits over just having toJson/fromJson on the classes directly:
It decouples the serialization logic from the actual classes. That means better code readability because classes only contain methods that relate directly to the class — serializers can even be put into their own files.
Currently, extensions can't create constructors. So having serializers separately makes it possible to write serializers for existing classes, like String or Flutter's Color, where you can't just add a fromColor constructor.
Both these points combined mean it also works well with code generation — the classes are hand-written and the serializer can be generated in a separate file.
Code example:
class Fruit {
Fruit(this.name, this.color);
final String name;
final String color;
}
// in another file
class FruitSerializer extends Serializer<Fruit> {
Map<String, dynamic> toJson(Fruit fruit) {
return ...;
}
Fruit fromJson(Map<String, dynamic> data) {
return Fruit(...);
}
}
An then also pass the serializer to the code that needs it:
someMethod<T>(Serializer<T> serializer, T value) {
...
}
someMethod(FruitSerializer(), someFruit);
final fruit = recreateFruit(FruitSerializer());
Obviously, you can't pass an object that can't be serialized to the code, because the method expects a Serializer<T>.
Can someone explain me the difference between both interfaces InputFilterAwareInterface and InputFilterProviderInterface? Both seem to serve to the same purpose, to get an InputFilter, but I know they cannot be the same... And when do they get called?
Thanks
Both interfaces exist for different purposes. The InputFilterAwareInterface guarantees that implemented classes will have a setInputFilter() and getInputFilter() methods which accept and return an InputFilter instance when necessary. On the other hand, the InputFilterProviderInterface guarantees only that implemented classes will have a getInputFilterSpecification() method which returns a filter specification (configuration array) which is ready to use as argument in various input factories.
For example; the snippet below came from Zend\Form\Form.php class:
if ($fieldset === $this && $fieldset instanceof InputFilterProviderInterface) {
foreach ($fieldset->getInputFilterSpecification() as $name => $spec) {
$input = $inputFactory->createInput($spec);
$inputFilter->add($input, $name);
}
}
As you can see, the Form class creates inputs and binds them to related filter using given specification which is returned by getInputFilterSpecification() method of the implementing class ($fieldset int this case).
Using Traits
Zend Framework 2 also provides lot of traits for commonly used interfaces. For example InputFilterAwareTrait for InputFilterInterface. This means, you can easily implement that interface if you have PHP >= 5.4
namespace MyNamespace;
use Zend\InputFilter\InputFilterInterface;
MyClass implements InputFilterInterface {
// Here is the trait which provides set and getInputFilter methods
// with a protected $inputFilter attribute to all MyClass instances.
use \Zend\InputFilter\InputFilterAwareTrait;
// Your other methods.
...
}
Now anywhere in your code, you can do this:
$myClass->setInputFilter($AnInputFilterInstance);
$myClass->getInputFilter(); // Returns an inputfilter instance.
As you can imagine, no trait exists for InputFilterProviderInterface because its responsibility is only returning a valid config spec. It doesn't deal with any instance or class attribute like is forced in InputFilterInterface.
I have written a list() method for retrieving a list of domain class instances matching a set of filters, and this method is used for different domain classes ; the code is exactly the same except the class on which the GORM methods are called :
Store => Store.createCriteria()
Client => Client.createCriteria()
and so on.
To avoid code duplication, I have tried to make a generic version of the list method, by creating a generic class :
class QueryUtils<T> {
def list(params) {
T.createCriteria()
[...]
}
}
This way, each of my services (StoreService, ClientService, etc) can extend QueryUtils :
class StoreService extends QueryUtils<Store>
class ClientService extends QueryUtils<ClientService>
and so on, and inherit the list() method corresponding to its domain class type.
The problem is that during the execution, it doesn't work since the effective type of T is java.lang.Object, instead of the domain class type I have specified :
groovy.lang.MissingMethodException: No signature of method: static java.lang.Object.createCriteria() is applicable for argument types: () values: []
Do you know how to solve this problem?
I did something like this a while back for Hibernate (outside of Grails) - https://burtbeckwith.com/blog/?p=21
But it doesn't work with Groovy since the compiler ignores generics. But you could change it to take the class in the constructor instead of as a generic type:
class QueryUtils {
private final Class clazz
QueryUtils(Class clazz) {
this.clazz = clazz
}
def list(params) {
clazz.createCriteria()
[...]
}
}