Using composition in custom controllers

Custom controllers quickly lose cohesion as data to display is mixed with actions. And custom controllers for wizards (see e.g. Creating a Wizard) are truly ugly as the data and actions for the sequence of pages are added to the single controller class. Also I recently had to implement a wizard that had some pages in common with an existing wizard so that re-use became an issue.

My conclusion is that using more classes is the way to go. This is particularly true for wizards, where the logic is best broken up per page, but also applies to controllers for single pages where the logic might be broken up per page section. Using more classes also benefits the design of the corresponding unit tests.

Here is a contrived example of the wizard case:

public class AbcWizardController {

    public PageOneController pageOne { get; set; }
    public PageTwoController pageTwo { get; set; }

    public AbcWizardController() {
        pageOne = new PageOneController();
        pageTwo = new PageTwoController();
    }
    ...
}

public class PageOneController() {

    public String title {
        get {
            return 'Page One';
        }
    }

    public List<CustomType__c> rows {
        ...
    }

    ...
}

// PageOne.page
<apex:page controller="AbcWizardController">
    <apex:pageBlock>
        <apex:pageBlockSection title="{!pageOne.title}">
            <apex:pageBlockTable value="{!pageOne.rows}" var="row">
                <apex:column value="{!row.Name}"/>
                <apex:column value="{!row.CustomField1__c}"/>
                <apex:column value="{!row.CustomField2__c}"/>
            </apex:pageBlockTable>
        </apex:pageBlockSection>
    </apex:pageBlock>
</apex:page>

A further benefit is that parameter passing to Visualforce components becomes cleaner too. Suppose you have a Visualforce page that presents groups of information that have the same structure and so want to create a Visualforce component for how that information is presented. The parameter passing to that component becomes simple if there is also a corresponding class (SectionController here) nested in the main controller class (XyzController here):

<apex:page controller="XyzController">
    <apex:pageBlock>
        <c:CustomSection section="{!sectionOne}"/>
        <c:CustomSection section="{!sectionTwo}"/>
    </apex:pageBlock>
</apex:page>

// Section.component
<apex:component>

    <apex:attribute name="section" type="SectionController" required="true" description=""/>

    <apex:pageBlockSection title="{!section.title}">
        <apex:pageBlockTable value="{!section.rows}" var="row">
            <apex:column value="{!row.Name}"/>
            <apex:column value="{!row.CustomField1__c}"/>
            <apex:column value="{!row.CustomField2__c}"/>
        </apex:pageBlockTable>
    </apex:pageBlockSection>
</apex:component>

Should the nested classes be made inner classes of the main controller class? There is little benefit to doing that as inner classes can’t see the members of their containing class. So to keep the re-use option open I suggest making the nested classes separate top-level classes. If the nested classes need access to the main controller class remove the direct coupling by using an interface:

public class XyzController implements PqrInterface {

    public SectionController sectionOne { get; set; }
    public SectionController sectionTwo { get; set; }

    public XyzController() {
        sectionOne = new SectionController(this);
        sectionOne = new SectionController(this);
    }
    ...
}

public class SectionController {

    private PqrInterface pqr;

    public SectionController(PqrInterface pqr) {
        this.pqr = pqr;
    }

    ...
}
Advertisements

One thought on “Using composition in custom controllers

  1. Superb article.. there’s always some difficulty in deciding how to organise and architect the units of an application, I think you’ve gotten it spot on.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s