170 likes | 185 Views
Learn about the Universal Properties Pattern, a new way to design object models that allows for flexibility, scalability, and easy inheritance. This pattern is particularly useful for NoSQL databases and relational databases that need to handle variable user content.
E N D
The Properties Pattern Based on http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html
Introduction • Many names • Prototype • Property List • Properties Object • Adaptive Object Model • Bottom line • Seems like a low-level hack • Actually, a new way to look at the world
Map API • get(key) • put(key, value) • has(key) • remove(key) • Also: keys()
Properties Pattern API • Based on the Map API • Enhancement: a parent() link • Points at another Properties object • Supplies additional properties • Map method need to be adjusted
public class PropList { private PropList parent; private Map<String, Object> map = new HashMap<String, Object>(); public PropList(PropList parent) { this.parent = parent; } public Object get(String key) { if(map.containsKey(key)) return map.get(key); if(parent != null) return parent.get(key); return null; } public void put(String key, Object value) { map.put(key, value); }
public boolean has(String key) { return map.containsKey(key) || parent != null && parent.has(key); } public void remove(String key) { map.remove(key); } public Collection<String> keys() { List<String> result = new ArrayList<String>(); result.addAll(map.keySet()); if(parent != null) result.addAll(parent.keys()); return result; } public PropList parent() { return parent; } }
Motivation • NoSql Databases (BigTable, HBase, …) • Relational DB: change resistant, do not scale well • New storage facilities are based on triplets: key, attribute, value • Property objects are the ideal in-memory counterpart • Flexible list of properties • Variable user content • When the logic is not interested in specific properties • It is interested in the relationship between the properties • Examples: A movie list browser, protein-peptide browser
org.eclipse.jdt.core.dom.ASTNode Each AST node is capable of carrying an open-ended collection of client-defined properties. Newly created nodes have none. getProperty and setProperty are used to access these properties.
Implementation • Representation of keys • String (almost always) • Arbitrary objects • Instances of a Key class • String + Numbers • Representation of key-value pairs • Linked list of pairs • Hashtable
Deletion • Three alternative • Remove from current instance • Remove also from its complete parent chain • Remove replaces the value with a special “not here” value
public class PropList { private static final Object NOT_HERE = new Object(); public void remove(String key) { map.put(key, NOT_HERE); } public Object get(String key) { if(map.containsKey(key)) { Object o = map.get(key); if(o == NOT_HERE) o = null; return o; } if(parent != null) return parent.get(key); return null; } // Similar changes in has() method
Safety & Invariants • A parent is notified of put() actions on any child • Can veto the change
public class PropList { public interface Interceptor { public Object onPut(PropList p, String key, Object value); } private List<Interceptor> interceptors = new ArrayList<Interceptor>(); public void addInterceptor(Interceptor i) { interceptors.add(i); } public void put(String key, Object value) { for(PropList p = this; p != null; p = p.parent) for(Interceptor i : p.interceptors) value = i.onPut(p, key, value); map.put(key, value); }
Persistence • Quite easy if all values are primitive • E.g.: One key/value pair per line (Otherwise) • Maintain a unique id property (primitive or string) • Saving • If value is another PropList save its id • Loading • Build an id -> PropList map • In each PropList replace properties that hold an id with the actual reference • Maps nicely to a ternary table on a relational DB: id, key, value
Motivation: Object-level inheritance • Sometimes we want to extend an object! • Here’s an existing instance • I want some new instance to be essentially the same as the existing one • Except for some additions, changes • When the existing instance changes I want the new ones to change as well • “I want that one” • “Inheritance by example”
Example: Disabling of Menus (cont.) • No open file -> Nine menu items are disabled • Class-level inheritance • A superclass, S, with setEnabled(boolean) method • Each menu item is an instance of this superclass • When a file is closed call setEnabled(false) on all nine instances • Object-level inheriance • A PropList object, x, with an "enabled" property • Each menu items is a PropList object, with x being the parent • When a file is closed: x.put("enabled", false)