How to control access to elements of collection properties?

103 views
Skip to first unread message

Philipp S

unread,
Aug 9, 2016, 7:35:36 AM8/9/16
to Dart Misc
Hey!

If I want to control access to a regular class property, I implement a getter and setter for it. However, sometimes I have a class which exposes a collection of elements through a property (in my example, `Basket.items`):
class Item {
 
Basket basket;
 
String name;
 
Item(this.basket, this.name);
}

class Basket {
 
final Map<String, Item> items = {};
 
void doStuffWithItems() { ... }
}

Let's say I want to create items on demand, and enforce relational integrity between items and baskets; I can achieve this by wrapping `Item.basket` with getters and setters, and implement explicit `Basket.getItem` and `Basket.addItem` methods:
class Item {
 
Basket _basket;
 
String name;

 
Basket get basket => _basket;

 
void set basket(Basket basket) {
    _basket
._items.remove(name);
    basket
._items[name] = this;
    _basket
= basket;
 
}

 
Item._(this._basket, this.name);
}

class Basket {
 
final Map<String, Item> _items = {};

 
Item getItem(String name) =>
      _items
.putIfAbsent(name, () => new Item._(this, name));

 
void addItem(Item item) => item.basket = this;

 
...
}

This works, but the `getItem` and `setItem` methods feel inelegant and not Dart-y.
Also, this way I lose the Map interface, so I can't do things like `basket.items.keys.map(print)`. I can fix that too by adding an `UnmodifiableMapView get items` to Basket, but now the API to obtain an item is scattered over two different objects!

If anonymous classes were supported, I'd go with an anonymous implementation of Map that overrides `operator []`:
class Basket {
 
final Map<String, Item> items = new Map<String, Item>() {
   
Item operator [](String name) =>
        putIfAbsent
(name, () => new Item(Basket.this, name)); // `Basket.this` in Java captures the object scope of the surrounding class, if you don't know (I didn't 10 minutes ago)
 
};

 
...
}

Since I've seen that anonymous classes are not supported and not planned, and I couldn't come up with a clever google search query about my issue, I thought I'd ask here. Does Dart have an established way to handle this use case, and if not, what are your preferences and experiences? Do you prefer
  * `getItem`, `setItem` and `UnmodifiableMapView` in Basket,
  * a regular (non-anonymous) class that implements the desired behaviour, and is only used for this one property,
  * or something I haven't thought of?

Thank you so much!
Best regards,
Philipp

Filipe Morgado

unread,
Aug 9, 2016, 10:57:02 AM8/9/16
to Dart Misc
Hi,

Personally, I like 'getItem', 'setItem', etc.
It's probably the most efficient and easy to implement.
It reminds me of 'addChild', 'addChildAt', 'removeChild', etc ... from AS3 display list.

However, if there are a lot of lists of maps everywhere which require some background processing when items are added/removed, we end-up with a lot of duplication.
So I used a custom List once:

typedef void ItemCallback<T>(T item);

class CustomList<T> implements List {
 
final List<T> _source;
 
final ItemCallback _itemAdded;
 
final ItemCallback _itemRemoved;
 
 
const CustomList(List<T> source, this._itemAdded, this._itemRemoved):
        _source
= source == null ? <T>[] : source;
 
 
void add(T item) {
    _source
.add(item);
    _itemAdded
(item);
 
}
 
 
void remove(T item) {
    _source
.remove(item);
    _itemRemoved
(item);
 
}
 
 
// Here goes complete List implementation ...
}

class Child {}
class Filter {}

class Container {
 
final _children = <Child>[];
 
CustomList _childrenList;
 
List get children => _childrenList;
 
  final _filters = <Filter>[];
  CustomList _filtersList;
 
List get filters => _filtersList;
 
  Container() {
    _childrenList
= new CustomList<Child>(_children, _onChildAdded, _onChildRemoved);
    _filtersList
= new CustomList<Filter>(_filters, _onFilterAdded, _onFilterRemoved);
 
}
 
 
void _onChildAdded() {
   
// process added child
 
}
 
 
void _onChildRemoved() {
   
// process removed child
 
}

 
void _onFilterAdded() {
   
// process added filter
 
}

 
void _onFilterRemoved() {
   
// process removed filter
 
}
}

void main() {
 
final container = new Container();
  container
.children.add(new Child());
  constainer
.filters.add(new Filter());
}

The container may keep a reference to the underlying lists, so it may efficiently cycle them.

I've used something like this in an experimental WebGL engine and it's the only place I've needed something similar (in Dart).
My custom implementation is here, and it's used herehere and here.
The code is a little old and there's some cleanup to do. There are caveats (probably shouldn't mess with the list in an 'added' callback). Not everybody will like it.
I actually didn't implement List, which would require 10x more code and testing. I just needed 'add', 'remove' and 'operator []'.

Others will have better solutions, but I hope this one helps.

P.S. The code above probably contains errors.

tatumizer-v0.2

unread,
Aug 9, 2016, 12:45:11 PM8/9/16
to Dart Misc

Filipe Morgado

unread,
Aug 9, 2016, 2:20:40 PM8/9/16
to Dart Misc
True!
That's the Darty way.

I try to limit my dependencies because code size escalates quickly and the more I import, the more unresponsive the analyzer used to get.
And we gain a few FPS in a display engine by eliminating abstractions.

Lasse R.H. Nielsen

unread,
Aug 12, 2016, 6:55:35 AM8/12/16
to mi...@dartlang.org
From a purely (my!) style point of view, I would probably never expose a Map property directly.
You have a class representing some abstraction. Its methods should be written in terms of that abstraction. Exposing a Map is revealing an implementation detail (there is nothing in the "Basket" name that suggests that baskets have mappings). 

/L


--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.



--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

Philipp S

unread,
Aug 17, 2016, 12:51:38 PM8/17/16
to Dart Misc
Hey!
Thanks for your answers, and please excuse the delay on my side :(

Maybe you are looking for delegating versions of list/map?
Thanks for the hint, I didn't know about that package at all, it looks promising!

From a purely (my!) style point of view, I would probably never expose a Map property directly. [...] there is nothing in the "Basket" name that suggests that baskets have mappings
Would've been better to come up with an appropriate example earlier, but I could think of one only now: `HtmlElement.attributes`[1] from dart:html and `ClassMirror.declarations`[2] from dart:mirrors both expose properties of type Map, with custom behavior tied to them.
What I'd prefer is such a customized map, but with as little code overhead as possible. I'll probably try around with extending DelegatingMap, and adding explicit getters and setters to the parent class, in the next days, and report back if I stumble upon a solution that really satisfies me :)

Best regards
Philipp

Reply all
Reply to author
Forward
0 new messages