Dart Named constructor vs Static method what to prefer? - flutter

So after dart made new keyword optional,
we can initialize an object with exact same syntax but different internal implementation.
class Color {
int r = 0, g = 0, b = 0;
Color({this.r, this.b, this.g});
//Named constructors
Color.red() //Implementation
Color.cyan() //Implementation
// Static Initializers
static Color red() => //Initialze with parameter
static Color cyan() => //Initialze with parameter
}
We can use them like this regardless of being it a named constructor or static method:
Color red = Color.red();
Color cyan = Color.cyan();
What is the place to use each of them?

In practice there is little difference between a factory constructor and a static method.
For a generic class, it changes where you can (and must) write a type parameter:
class Box<T> {
T value;
Box._(this.value);
factory Box.withValue(this.value) => Box<T>._(value);
static Box<T> fromValue<T>(T value) => Box<T>._(value);
}
...
var box1 = Box<int>.withValue(1);
var box2 = Box.fromValue<int>(2);
So, for generic classes, factory constructors are often what you want. They have the most pleasant syntax.
For non-generic classes, there is very little difference, so it's mainly about signaling intent. And deciding which category the name goes into in the DartDoc.
If the main objective of the function is to create a new object, make it a constructor.
If the main objective is to do some computation and eventually return an object (even if it's a new object), make it a static function.
That's why parse methods are generally static functions.
In short, do what feels right for your API.

Constructors and static functions are different. You usually create a named constructor that returns an instance of an object with some predefined values. For example, you have a class called Person which stores Name and Job. You can create this named constructor Person.doctor(name) which you will return a Person object with Job = 'doctor'
class Person{
final name;
final job;
Person(this.name, this.job);
Person.doctor(this.name, {this.job = "doctor"});
}
Static functions or variable persists on all the instance of a class. Let us say, Person has a static variable called count. You increment the count variable whenever an instance of Person is created. You can call Person.count anywhere later in your code to get the value of count (Number of instances of Person)
class Person{
final name;
final job;
static int count;
Person(this.name, this.job){
count++;
}
Person.doctor(this.name, {this.job = "doctor"});
}

Another very useful feature of static class methods is that you can make them asynchronous, i.e. wait for full initialisation in case this depends on some asynchronous operation:
Future<double> getCurrentPrice(String ticker) async {
double price;
// for example, fetch current price from API
price = 582.18;
return price;
}
class Stock {
String ticker;
double currentPrice=0.0;
Stock._(this.ticker);
static Future<Stock> stockWithCurrentPrice(String ticker) async {
Stock stock = Stock._(ticker);
stock.currentPrice = await getCurrentPrice (ticker);
return stock;
}
}
void main() async {
Stock stock = await Stock.stockWithCurrentPrice('AAPL');
print ('${stock.ticker}: ${stock.currentPrice}');
}

Another benefit of the distinction between named constructor and static function is that in the documentation generated the function will be either filed in the construction section or the methods section, which further makes it's intentions clearer to the reader.
A person looking for a constructor in the constructor section of the documentation will easily discover the named constructors as opposed to having to also dig through the static functions section too.

Related

Dart convert data to custom class

I have a function Future<T?> getDocById<T>(String id) aysnc {}, it gets the data from web and return the value with type T. How can I convert any incoming data from web to a given type?
use case - class User{ String name; User(this.name);}
now, await getDocById<User>() how can I convert the data to type User inside getDocById function? in another case - getDocById<Boo> it should get data from web and convert it to Boo and return
Since I just answered a similar question I am copying the code into here. However, if you have an unknown T incoming, you need to distinguish cases based on what is incoming. Without this you want be able to convert a bool or int into a String name.
Create a method in one of the classes
class A {
A(this.a);
final int a;
B toB() {
return B(
b: a,
}
}
class B {
B(this.b);
final int b;
}
To be used like final bInstance = aInstance.toB(); meaning you declared somewhere aInstance = A(1);

Dart - Way to access a inherited static property from a parent class method

In PHP there is a way of accessing a static property value that is defined/overridden on an inheritor.
e.g.
class Foo {
public static $name='Foo';
public function who(){
echo static::$name;//the static operator
}
}
class Bar extends Foo {
public static $name='Bar';
}
$bar = new Bar();
$bar->who();
//Prints "Bar";
Is there ANY way of doing the exact same thing in Dart language?
Addressing comments:
About making it instance prop/method: There's a reason for the existence of static properties and methods and it's not having to create a new instance of the object to access a value or functionality that is not mutable.
Yes, but that's not how you are using it. Your use case is to invoke the method on an object, and therefore you really want an instance method. Now, some languages automatically allow invoking class methods as instance methods, and I see two choices for a language that offers that ability:
Statically transform fooInstance.classMethod() to ClassFoo.classMethod() based on the declared type (not the runtime type) of the object. This is what Java and C++ do.
Implicitly generate virtual instance methods that call the class method. This would allow fooInstance.classMethod() to invoke the appropriate method based on the runtime type of the object. For example, given:
class Foo {
static void f() => print('Foo.f');
}
You instead could write:
class Foo {
static void classMethod() => print('Foo.f');
final instanceMethod = classMethod;
}
and then you either could call Foo.classMethod() or Foo().instanceMethod() and do the same thing.
In either case, it's syntactic sugar and therefore isn't anything that you couldn't do yourself by being more verbose.
About the "meaning of static" and "only work because they allow invoking class methods as instance methods" : That affirmation is actually wrong. In the case of PHP, as per the example above, the Language is providing a way to access the TYPE of the class calling the method in the inheritance chain. A(methodA) >B > C. When C calls methodA, PHP allows you to know that the class type you're in is indeed C, but there's no object instance attached to it. the word "static" there is a replacement for the caller class type itself
All of that is still known at compilation time. That C derives from B derives from A is statically known, so when you try to invoke C.methodA, the compiler knows that it needs to look for methodA in B and then in A. There's no dynamic dispatch that occurs at runtime; that is still compile-time syntactic sugar. That is, if you wanted, you could explicitly write:
class A {
static void methodA() {}
}
class B extends A {
static void methodA() => A.methodA();
}
class C extends B {
static void methodA() => B.methodA();
}
Anyway, in your example, you could write:
class Foo {
static String name = 'Foo';
String get nameFromInstance => name;
void who() {
print(nameFromInstance);
}
}
class Bar extends Foo {
static String name = 'Bar';
#override
String get nameFromInstance => name;
}
void main() {
var bar = Bar();
bar.who(); // Prints: Bar
}

How to recreate singleton instance if different params are passed to the constructor in dart

I gathered the following understanding for creating a singleton in dart with params
class Foo extends ChangeNotifier {
late String channel;
void instanceMemberFunction () {
print('Foo created with channel $channel')
}
static final Foo _instance = Foo._internal();
Foo._internal() {
instanceMemberFunction();
}
factory Foo({
required String channel
}) {
_instance.channel = channel;
return _instance;
}
}
and I am calling the instance like so
Foo({channel: "bar"})
Now I want to have some working that if I use
Foo({channel: "baz"})
Then a new instance is created and it's okay in that case to destroy the old one. How can I achieve this in dart?
It seems like you've copied some existing example for creating a singleton without fully understanding what it's doing and why. The core parts are:
The single instance is stored in a global or static variable.
The class has one or more public factory constructors that returns that global/static variable, initializing it if necessary.
All other constructors for the class are private to force consumers to go through the factory constructors.
Therefore, if you want your factory constructor to replace its singleton based on its argument, you need to:
Make your factory constructor check if the argument is appropriate for the existing instance. If it is, return the existing instance. If not (or if there is no existing instance), create and return a new instance.
Since you need to check if the existing instance is initialized, make it nullable. (You alternatively could initialize it to a non-null sentinel value, e.g. Foo._internal(channel: '').
Pass the argument along to the private constructor.
class Foo extends ChangeNotifier {
final String channel;
void instanceMemberFunction () {
print('Foo created with channel $channel');
}
static Foo? _instance;
Foo._internal({required this.channel}) {
instanceMemberFunction();
}
factory Foo({required String channel}) {
if (channel != _instance?.channel) {
_instance = Foo._internal(channel: channel);
}
return _instance!;
}
}
Note that this implementation will create a new object if the constructor argument changes, which isn't very singleton-like. Depending on what you want to do, you could:
Return a new object (which could allow multiple simultaneous instances).
Return the existing object.
Return the existing object, but mutate it with the constructor argument.

why should i make the returned instance of factory a static field?

Creating a function that returns an instance of the class is right, but creating a factory that returns an instance of the class should be a static field. why should i make the returned instance of factory a static field?
the code is:
class DBHelper{
DBHelper._();
factory DBHelper()=>instance; // ->> cursor error
// static
// final
DBHelper instance=DBHelper._();
int number=2;
int fun()=>number;
}
I think you didn't quite get the purpose of factory methods. The example you've posted is the way of using singleton pattern in dart/flutter. It's, in my opinion the cleanest way of creating a singleton class.
Now the factory method can be used to to simplify the construction of objects and they don't have to return static fields at all. Here are a few examples:
class OneObject {
final int one;
final String two;
final double three;
OneObject(this.one, this.two, this.three);
factory OneObject.withoudTwo(int one, double three) {
return OneObject(one, "2", three);
}
factory OneObject.fromJson(Map<String, dynamic> aMap) {
return OneObject(aMap["one"], aMap["two"], aMap["three"]); // this is bad use of json parsing
}
}
In your case the DBHelper._(); is the actual constructor of the class and the _ makes it private and this allows you to create a factory method called DBHelper.();
To elaborate the answer a bit more, factory methods are similar to static methods and static methods can't access instance members of the class that they are declared on, they can access only static members. In your example, the instance property (without static) will be created only when a new instance of the DbHelper is created and because of that, returning it from a factory method is impossible because it's not available yet.

How to return multiple types of class types from single generic class in dart flutter?

I have multiple class like this:-
Class A {
static int xyz = 10;
int c;
int d;
static A getData() {
// Do something
return A()..c = xyz*5;
}
Class B {
static int abc = 10;
int c;
static B getData() {
// Do something
return B()..c = xyz*5;
}
So, here you can see that the the getData() is doing the same thing, but have different return types.
Is there any way to avoid duplicate implementation like this, can it be done by defining a single function which can reference the class and have multiple return type?
This has two parts: creating the object, and assigning to a field of the object.
Creating the object, you are mostly out of luck. The only way to create an object of a specific type in a generic method is by using reflection via dart:mirrors. However, you have indicated that this is for a Flutter project, and Flutter doesn't support reflection, so that isn't an option. The only way you are going to be able to dynamically create an object is to pass in a factory method that the generic method can call to construct the object.
Assigning to a field of the object is easier, but it requires that you either lose static type checking by using dynamic or by tying your classes together with inheritance. The latter is the preferable choice, but if you are working with a library than it isn't always an option.
Combining these two things, the code will look like this:
class Foo {
static int xyz = 10;
int c;
}
class A extends Foo {
int d;
static A getData() {
return modifyObject(() => A());
}
}
class B extends Foo {
static B getData() {
return modifyObject(() => B());
}
}
T modifyObject<T extends Foo>(T create()) {
return create()..c = Foo.xyz * 5;
}
Before doing this, though, I'd take a look at whether your project actually needs it. If your use case is as simple as your example, I would argue that this level of generalization is overkill and you are hurting your code's readability more than you are helping its modularity.