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