Skip to content

Instantly share code, notes, and snippets.

@nlundquist
Created June 9, 2017 15:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nlundquist/85467d08a4cc2868e4c6146e0bdaee2b to your computer and use it in GitHub Desktop.
Save nlundquist/85467d08a4cc2868e4c6146e0bdaee2b to your computer and use it in GitHub Desktop.
can-connect behavior mutual interface ordering problem
Mutual Interface Ordering Problem:
Two behaviors (A1, A2) implement the same interface properties. A third interface (B) depends on that interface, but requires being ordered after a specific implementation of A. How can we allow arbitrary insertion order of these behaviors and still end up with the dependency correctly satsified?
For the purpose of the example below B depends on being ordered after A1.
e.g
We should be able run code like:
`
var con = connect();
con.insert(B);
con.insert(A2);
con.insert(A1);
`
and end up with the connection having the behaviors ordered A1, B, A2.
Real world examples of this include:
How cache-requests (which implements getList and depends on getList) needs to be ordered after data/url (which also implements getList) but before data/callbacks (which also implements getList). A potential issue arising from misordering would be requests fulfilled by the cache not triggering data callbacks.
How data/parse depends on data/url (both offer the same interface), but constructor/constructor must follow the implementation offered by data/parse (since it returns a format constructor/constructor can utilize).
Since we want to allow behavior insertion in any order, and when evaluating positions by interface alone there are multiple positions that satisfy the dependency of B, solutions to this problem require some sort of ordering based on something other than the interface properties.
There are a few types of solution possible:
- canonical behavior ordering
- explicit behavior ordering
- categorized behavior ordering
- prioritized behavior ordering
- more ordering rules
Canonical Ordering:
What we have now, a single master ordering of behaviors that define specifically how behaviors should be arranged.
Pros:
- Easiest to implement and understand
Cons:
- Requires a custom behavior creator to modify canonical order
- Chance for error when creating canonical order
Explicit Ordering:
Behaviors define specifically what behaviors they must follow by name. Not much different than canonical ordering.
Pros:
- Easy to implement
- Doesn't require an ordering other than what's specified by the behavior
Cons:
- Harder to modify
- insertion of a custom behavior between existing behaviors requires the creator modify requirements specified by other behaviors or use the name of an existing behavior
- Chance for error when creating explicit order
Categorized Ordering:
Behaviors are assigned to categories that indicate what range of the prototype chain they should be positioned in.
e.g
data/url is part of a 'data provider' category
data/parse is part of a 'data mutator' category
constructor/constructor is part of a 'instance provider' category
the hierarchy of categories indicates data providers are at the highest position in the prototype chain, followed by data mutators and then instance providers
thus no matter the order of insertion these behaviors are always ordered as expected
Pros:
- Easy to implement
Cons:
- Insertion of a custom behaviors between existing categories likely requires adding a new category of behaviors to the heirarchy
- Still requires a canonical order of categories
- Chance for error when assigning behavior to category or ordering categories
Prioritized Ordering:
Behaviors are defined with a priority (eg. on a scale of 1 - 100000) which indicates how far up the prototype chain they should be positioned.
e.g
A1 and A2 implement the same interface,
A2 depends on A1,
B depends specifically on the interface provided by A1,
Desired ordering is A1, B, A2
A1 is priority 10000,
A2 is priority 8000,
B is priority 9000
`
var con = connect();
con.insert(B); // is inserted as sole behavior
con.insert(A2); // is inserted after B due to B's higher priority
con.insert(A1); // is inserted before B due to A1's higher priority
// con proto from top to bottom === A1, B, A2
`
Pros:
- Easy & flexible insertion and replacement
- Easy to implement
- Doesn't require an ordering other than what's specified by the behavior
- Easy to move existing behaviors by reassiging their priorities
- unlikely to be require in most use cases
Cons:
- Somewhat harder to understand, but easy once grasped
- Potential for "crowding" a region of the priority range
- Chance for error when assigning priority
More Ordering Rules:
Rather than just positioning a behavior where it satisfies its interface requirements, we provide addition positoning directives so that it ends up where it's actually required.
Additional directives like:
- what interface needs to follow the behavior
- if it should be inserted as high as possible, or low as possible in the ordering
- etc.
Pros:
- Doesn't require a prexisting ordering
Cons:
- Complex and difficult to understand
- Difficult for custom behvaior creators to use
- Can't think of an implementation that is completely unaffected by insertion order
- Insertion rules of existing behaviors may need to be re-evaluated when adding later behaviors
- Chance for error when creating ordering rules
Recommendations:
- Continue using a canonical ordering, exposing it for custom behavior creators to modify. This is the easiest to understand and implement and resolves the issue conclusively. However more overhead since custom behavior creators will need to modify the canonical ordering.
or,
- Adopt prioritized ordering. This is easy for custom behavior creators to understand, doesn't require an ordering other than what's specified by the behavior, providing somewhat more flexibility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment