• If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • You already know Dokkio is an AI-powered assistant to organize & manage your digital files & messages. Very soon, Dokkio will support Outlook as well as One Drive. Check it out today!

View
 

Basics-Design Patterns

Page history last edited by Paul Lambert 14 years, 1 month ago
  1. SproutCore is built on some consistent design and naming patterns that are used throughout the framework.  
    1. If you learn these design patterns, you will more easily understand how SproutCore classes are meant to be used.  
    2. Incorporating these patterns into your own app can make your code better as well
  2. MVC
    1. The overarching design pattern behind SproutCore is MVC.  See the SproutCore Architecture Overview section for more info on this.
  3. Booleans
    1. isFoo - used to designate a property name that is a boolean flag.   Represents a "state" of the object.  Usually these can change over the lifetime of the object.
      1. ex: isEnabled, isVisible, isReady
    2. canFoo - also a boolean flag but usually enables or disables a feature.  Usually this is set when the object is first instantiated and does not change.
      1. ex: canScrollHorizontal, canReorderContent
    3. hasFoo - boolean flag indicating whether a particular component is available or not.  Often used to describe aggregate relationships.  
      1. ex: hasHorizontalScroller, hasVisibleChildren
  4. Observers
    1. Observers are methods that are called whenever a particular property changes
    2. Most common observer is a "local observer", which is a method on an object that is called whenever a property on the same instance changes.
    3. Observers usually end in the phrase "DidChange" after the property it observes.  i.e. contentDidChange, valueDidChange.
    4. If a single observer is called for several different properties, you could name it something different like:  relatedValuesDidChange()
    5. Sometimes you may need to call an observer directly.  This should not happen often, but it is OK if needed.
  5. Bindings
    1. Bindings connect the properties on two objects together.  Whenever one property changes, the other property will be updated with the same (or an optionally transformed) value.
    2. Bindings are used mostly to connect views to controllers and controllers to controllers.  You should rarely use bindings for your models or to connect views to other views.  Instead use observers. 
  6. Delegates
    1. We generally like to keep the number of classes in your application down when possible.  Too many classes encourage over-engineering.
    2. Instead, you "delegate" fine grained control of an object to another object.
    3. You can often set a "delegate" on SproutCore objects and the object will then call your delegate at key points in its operation to get permission to perform certain operations or to give you a chance to override the default behavior.
    4. Whenever you want to slightly tweak the behavior of a SproutCore class, especially a view, check the documentation for the existence of a delegate class.  If one exists, you can make a controller a delegate and then implement the delegate methods on the controller.
    5. When designing your own application, consider making your views more generic so they can be reused.  Implement application-specific business logic as delegates on your controller.
  7. Singletons
    1. A singleton is a one-off object that you define, with its own methods and properties, that will only exist one time in your application.
    2. Most controllers are singletons.
    3. In most other languages, you create a singleton, you must define a custom subclass then instantiate that class one time in your application.
    4. In JavaScript, you can add functions to objects just like any other property, so creating a singleton is much simpler.  Just do myObject = SC.Object.create({  property: foo, method: function() { ... } });
  8. Mixins
    1. A mixin is a static hash of methods and properties that can be added to an object or class.  You usually use mixins to define a set of common methods that you want several different classes to share without having them inherit from the same parent class.
    2. Mixins are a great way to use "aspect-oriented" programming techniques to build your classes.  When you extend or create an object, you can name one or more mixins.
    3. If you define initMixin() on a mixin, it will be called when the object is created. 
    4. For views, you can also define renderMixin() which will be called whenever the render() method is called on the view.  This allows you to add class names and styles to the root view.
    5. If you are defining a generic view or other object and you need other objects it depends upon to implement certain methods, you should define a mixin with default implementations of all of the methods.  This way other objects can simply apply the mixin and override only the functions they care about.
  9. Protocols
    1. An Informal Protocol is simply a set of methods that another object may call on your object if you implement them. 
    2. Unlike mixins, protocols are not actually included it production apps.  You only define them for documentation purposes.
    3. Protocols are often used when you want an object to potentially respond to many different methods but you don't want the object to have to define each and every one.  Default responder actions are a good example.  As is the drag and drop API.
  10. Actions/Responders
    1. Actions
      1. Actions are high-level, applications-specific events in your application.  Usually they are generated by views or by your data source.
      2. Actions are NOT usually low-level events generated by the browser such as mouseDown or keyDown.  These are referred to as events.
      3. Ex:  when you click on a button, the browser will send a mouseDown event which will be routed to the button.  The button in turn may be configured to send an action called "openPanel".
    2. Responders
      1. Whenever an action occurs in your app, it can be tricky to decide what code should run in response to the action.  SproutCore uses the Responder pattern to resolve this.
        1. In SproutCore you can have a responder context.  A context can receive actions.  
        2. Each context has a firstResponder.
        3. Whenever you send an action to a context, it will check the firstResponder to see if it implements a method with a corresponding name.
        4. The firstResponder may also have a nextResponder property.  If the firstResponder does not implement the method, the context will check the nextResponder.  If that does not implement the method, it will check the nextResponder's nextResponder and so on until it either finds a responder that implements the method or finds a responder with no nextResponder.
        5. If a responder implements a method matching an action but returns NO, it is treated like it did not implement the method.
        6. If you get to the end of the responder chain and the action was not handled, then the context will try a defaultResponder.  If the defaultResponder is another responder context, then the action will be sent down that context's responder chain.
      2. All the views in your application comprise one responder chain in your application.  Each pane in your app is its own responderContext.
      3. You can also make your app namespace a responder context and set firstResponders there.  By changing the firstResponder you can completely alter how various actions will be handled in your application.
      4. Responders are a convenient way to implement state management in your application.  You can actually design all of the states in your app using a statechart and then implement them using responders.
      5. One of the biggest benefits of this design pattern is that when an action occurs in your app that you did not plan for, it will fail in very obvious ways (i.e. the action will not be handled).  This leads to very reliable code.  
      6. In fact, this pattern is often used in mission critical software because of its known reliability.

         

  11. Computed Properties
  12. Enumerables
  13. value/content properties
  14. configurable keys
  15. RunLoop/deferred execution

Comments (0)

You don't have permission to comment on this page.