Keeping things DRY

dkarten's Avatar

dkarten

09 Sep, 2015 11:15 PM

Two questions about adhering to DRY as much as possible.

1) Add/Edit Views: When creating/editing something object in the application, the view is basically the same. If we are editing data, the view must be populated with data from the context (likely via an event), but if we are adding a new object, we want the form to be blank. How can we open the view and ask for stored data if we are in EDIT mode, but not do it in ADD mode? Right now I have given my view a public mode property, and adding event listeners in the mediator based on the mode but this seems like its violating the idea of a mediator.

2) I'm finding that a number of models I have set up have one-to-one representations with value objects that are passed around the application. This gave off a bit of a code smell. Here is an example that can perhaps explain the problem better. A lot of these models are store settings or preferences, not lists of data.

Let's say one function of the app is to create a report (e.g. a text report or pdf report) of some pieces of data tracked by the model tier. We want to be able to create the report in multiple file formats. The app can be configured to format the data printed on the reports. So if we wanted to print a list of customers, we want to have the option to print their name in "Last, First" format, or "First Last" format etc. These settings apply to ALL reports, so I feel like we want to keep these in a model. Implementing this in RL/MVC, this is what I have.

  1. ReportSettingsModel -- persist the settings to be applied to reports
  2. ReportSettingsVO -- shuttle configuration from the view to the model
  3. ReportSettingsView -- offer UI for to configure reports
  4. XXXReportFileService -- writes data to a report file (XXX could be PDF or TXT)

The problem: ReportSettingsVO and ReportSettingsModel are almost identical. This isn't DRY! It feels wrong. Is it? Should settings be tracked in value objects and injected into the models that need them? Could we create these VOs on the fly in commands, and the use injector.mapValue to apply it to all injection points?

  1. Support Staff 1 Posted by Ondina D.F. on 10 Sep, 2015 12:36 PM

    Ondina D.F.'s Avatar

    I don't know how you set the mode (state) of your view, when you add the view to the stage through view's API or on user interactions from within the view?

    Right now I have given my view a public mode property, and adding event listeners in the mediator based on the mode but this seems like its violating the idea of a mediator.

    Does this mean that you are asking the view about its state in mediator's initialize method?
    Like, if( view.mode=="editing" ) addContextListener for some event...?

    • If you don't like this approach, you can let the view dispatch an event when its state changes, and the mediator can remove/add event listeners like this:
    private function onViewStateChanged(event:ViewStateEvent):void
    {
        removeContextListener(SomeContextEvent.SOME_TYPE, onSomeType, SomeContextEvent);
    if(event.message=="editing")
        addContextListener(SomeContextEvent.SOME_TYPE, onSomeType, SomeContextEvent);
    }
    
    • or, the view can send a ViewStateEvent with 2 different types, ADD_LISTENERS, REMOVE_LISTENERS and the mediator would add or remove events in the corresponding handlers

    • another possibility is to let the view itself decide if it is interested in the data coming from the mediator depending on its state.

    • another option (for the case of adding views to the stage that have a predefined role):

    Create interfaces for your view, say, IAddThings, IEditThings and let the view implement both 'behaviours'. When you need an editing view you add it to the stage as IEditThings, and as IAddThings when you need it for other purposes...
    Create 2 mediators, one for the IAddThings and another for the IEditThings and inject IAddThings into AddThingsMediator and IEditThings into EditThingsMediator.
    Create 2 guards for the 2 mediators. The EditThingsMediator should be created if the view's state is "editing" (the view can be injected into the guard, which can interogate view's state).
    The AddThingsMediator should be created only if view's state is "adding" or is not "editing" or whatever condition you need to check.
    The mediators should add event listeners only for the events of interest to them.
    This solution might be the cleanest, but it is a bit more convoluted. Let me know if you need more clarification on this.

  2. Support Staff 2 Posted by Ondina D.F. on 10 Sep, 2015 03:42 PM

    Ondina D.F.'s Avatar

    For the use case you've presented I'd use a ReportSettingsModel holding a property 'report' of type ReportSettingsVO, as shown in the example below. The view is setting the values on a new ReportSettingsVO and is dispatching an event with a payload of type ReportSettingsVO.
    ReportSettingsCommand passes the event's payload to the model. When the GenerateReportCommand is triggered, it reads the model's properties and passes them onto the ReportFileService.
    So, ReportSettingsModel has a property report: ReportSettingsVO and it could contain other properties as well, as VOs or other types, but the 2 properties carried by the ReportSettingsVO are not part of the ReportSettingsModel.
    Tell me why this wouldn't work for you?

    public class ReportSettingsVO
    {
        protected var _reportType:String;
        protected var _rowsPerPage:Number;
    
        public function ReportSettingsVO(reportType:String, rowsPerPage:Number)
        {
            _reportType = reportType;
            _rowsPerPage = rowsPerPage;
        }
    
        public function get reportType():String
        {
            return _reportType;
        }
    
        public function get rowsPerPage():Number
        {
            return _rowsPerPage;
        }
    }
    

    ReportSettingsModel::

    public class ReportSettingsModel
    {
        private var _report:ReportSettingsVO;
    
        //private var _someOtherVO:SomeOtherVO;
        //private var anotherThing:String;
    
        public function ReportSettingsModel()
        {
        }
    
        public function get report():ReportSettingsVO
        {
            return _report;
        }
    
        public function set report(value:ReportSettingsVO):void
        {
            _report = value;
        }
    }
    

    ReportView - editing the report settings::

    var reportType:String = someComponent.value;//PDF/TXT
    var report:ReportSettingsVO = new ReportSettingsVO(reportType , 5);
    ....
    dispatchEvent(new ReportSettingsEvent(ReportSettingsEvent.REPORT_SETTINGS_CHANGED, report));
    

    ReportSettingsCommand::

    [Inject]
    public var event: ReportSettingsEvent;
    
    [Inject]
    public var model:ReportSettingsModel;
            
    public function execute():void
    {
        model.report = event.report;        
    }
    

    GenerateReportCommand, needing the report settings::

    [Inject]
    public var model:ReportSettingsModel;
    
    [Inject]
    public var service: ReportFileService;
        
    public function execute():void
    {
        service.doYourThing(model.report.reportType);
    //or just
        service.doYourThing(model.report);
    }
    

    Should settings be tracked in value objects and injected into the models that need them?

    Are you referring to the same scenario as the one above? If the answer is yes, then I'd say no, you shouldn't inject the vos into the models.

    If you're setting the VO's values once at the app start and you're not going to edit them later, then you could inject them wherever you needed them.

    Could we create these VOs on the fly in commands, and the use injector.mapValue to apply it to all injection points?

    Yes, you can create classes on the fly like this:

    var someVO:SomeVO = injector.getOrCreateNewInstance(SomeVO);
    if (!injector.hasMapping(SomeVO))
        injector.map(SomeVO).toValue(someVO);
    
    someVO.someProperty = "new value";
    

    but you have to decide whether it makes sense to use vos like this.
    You should know by now that injecting everything everywhere comes at a price!

  3. 3 Posted by dkarten on 11 Sep, 2015 07:10 PM

    dkarten's Avatar

    Once I had written out my question, the answer became more obvious. Thanks for confirming with your wisdom!

  4. dkarten closed this discussion on 11 Sep, 2015 07:10 PM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac