I don't think I used it once. I enjoyed exploring the proxy pattern in Dart. Trying to use isolates for simple remote proxies was probably ill-advised, but aside from that, my exploration went swimmingly. Except one thing that I was sure would happen never did.
Not once did I use the @proxy annotation for any of my proxy implementations. I am pretty sure that I understand what the
@proxy annotation does, but "pretty sure" pretty much always translates into some mistake on my part. And since I never once had to use it when implementing a variety of proxy classes, there is a good chance that I have a knowledge gap.I had always assumed that
@proxy annotated a class indicating that the class supported methods even if not specifically declared. Since Dart is optionally typed, this would be a static type analysis warning, not a compile or runtime issue. But I specifically run all my code through dartanalyzer before using it, so how did I avoid it?There would have been no need for the annotation with my websocket remote car implementation. All of the methods that were declared in the interface:
abstract class AsyncAuto {
String get state;
Future drive();
Future stop();
}Were explicitly declared in the remote proxy class:class ProxyCar implements AsyncAuto {
// ...
String get state => _state;
Future drive() => _send('drive');
Future stop() => _send('stop');
// ...
}But how did I manage to avoid @proxy with my noSuchMethod() / protection proxy class? In that example, I was still working with cars, but the subject of the pattern, the Automobile interface, only declared a single method:abstract class Automobile {
void drive();
}The proxy class in this example does not explicitly declare the drive method even though it implements the Automobile interface:class ProxyCar implements Automobile {
Driver _driver;
Car _car;
ProxyCar(this._driver);
Car get car => _car ??= new Car();
dynamic noSuchMethod(i) {
if (_driver.age <= 16)
throw new IllegalDriverException(_driver, "too young");
return reflect(car).delegate(i);
}
}
Without explicitly declaring drive(), I need @proxy, right? Well.. no. When I run dartanalyzer against this library and some client code, I get no issues in either:$ dartanalyzer bin/drive.dart lib/car.dart Analyzing [bin/drive.dart, lib/car.dart]... No issues found No issues foundI also get no errors when using this DartPad, so this seems to be expected and / or desired behavior. So what is the point of
@proxy then? As far as I can tell, it comes in handy when I lack a subject in the proxy pattern. That is, when you do not have an interface to implement, then warning will be issued. For example, if I remove the implements clause from the ProxyCar implementation, but leave the same noSuchMethod() implementation in place:class ProxyCar {
// ...
dynamic noSuchMethod(i) {
if (_driver.age <= 16)
throw new IllegalDriverException(_driver, "too young");
return reflect(car).delegate(i);
}
}
Then my client code: // ...
car = new ProxyCar(new Driver(25));
car.drive();
// ...Generates warnings:$ dartanalyzer bin/drive.dart lib/car.dart Analyzing [bin/drive.dart, lib/car.dart]... [hint] The method 'drive' is not defined for the class 'ProxyCar' (/home/chris/repos/design-patterns-in-dart/proxy/bin/drive.dart, line 11, col 7) 1 hint found.In this situation, I can eliminate the warning with a
@proxy annotation before the class:@proxy
class ProxyCar {
// ...
}But, as long as I have an interface to implement and a noSuchMethod() method declared, @proxy is not necessary. In The Dart Programming Language, Gilad Bracha has a nice explanation of why one might want a proxy without a classic subject. I may take a closer look at that tomorrow. For now, I have a better understanding of @proxy and why it usually is not necessary.
Play with the non-annotated code on DartPad: https://dartpad.dartlang.org/3577ddd84876ebf310c4.
Day #73
No comments:
Post a Comment