views:

67

answers:

1

Hi,

Say i have a class hierarchy of domain objects with one base class and a couple of child classes, one level.

Let say I have a list of those objects (list of the base class) and I want to apply some logic to the classes that I feel don't really belong to the classes (eg. design/UI specific code).

What are my alternatives ?

  1. If-is statement. Personally this one shouldn't even be considered as an alternative but i write it anyway.

  2. Polymorphism. This one is actually an alternative in some cases but with my example above, I don't want my classes to contain any UI specifics.

  3. Resolving some logic method via reflection/IoC container based on the type of the object.
    Eg C#. Type type = typeof(ILogic<>).MakeGenericType(domainObject.GetType());
    I really like this one, I don't get any compile time checks though if an implementation is missing for a sub class, or is that possible somehow?

  4. Visitor pattern. Will work but seemes kind of overkill to apply on a structure thats only one level deep.

Anyone has any other tips or tricks to solve these kinds of problems?

+2  A: 

Great question. Problem is that there are many solutions and most of them will work.

I work with MVC a lot, similar situation happens quite often. Especially in the view, when similar rendering needs to happen across some views... but does not really belong to the view.

Let's say we have child class ChildList that extends BaseList.

Add a property uiHandler in the child class. Overload the rendering function, let's say toString(), and use the uiHandler with your specific UI/Design things.

I wrote a little something, it is in PHP... but you should be able to get an idea. It gives you freedom to chose how your objects will be displayed and flexibility to use specific UIs for specific objects.

Look at the code below, it seems like a lot but int's not that bad.

  • BaseList - your base class
  • BaseListUIExtended - base class that uses UI, takes optional UI class as constructor parameter. In C# 4 you can use optional, otherwise use 2 constructors.
  • UIBase - interface for UI classes...
  • UIChildSpecific - UI class
  • ChildList - child class that can use UI or not, because of BaseListUIExtended optional constructor parameter.

Define interface

/**
 * Base UI interface
 */
interface IUIBase {

    /**
     * Renders the Base Class
     *
     * @param UIBase $obj
     * @return string
     */
    public function render($obj);

}

Define base classes, child class

//**************************************************************
//  Define Base Classes
//**************************************************************
/**
 * Base Class
 */
class BaseList {

    /**
     * List of items
     * @var array
     */
    protected $_items = array();

    /**
     * Gets collection of items
     *
     * @return array
     */
    public function getItems() {
        return $this->_items;
    }

    /**
     * Adds new item to the list
     * @param object $item 
     */
    public function add($item) {
        $this->_items[] = $item;
    }

    /**
     *  Displays object
     */
    public function display() {
        echo $this->toString();
    }

    /**
     * To String
     */
    public function __toString() {
        //  Will output list of elements separated by space
        echo implode(' ', $this->_items);
    }

}

/**
 * Extended BaseList, has UI handler
 * This way your base class stays the same. And you
 * can chose how you create your childer, with UI or without
 */
class BaseListUIExtended extends BaseList {

    /**
     * UI Handler
     * @var UIBase
     */
    protected $_uiHandler;

    /**
     * Default Constructor
     *
     * @param UIBase Optional UI parameter
     */
    public function __construct($ui = null) {

        //  Set the UI Handler
        $this->_uiHandler = $ui;
    }

    /**
     * Display object
     */
    public function display() {
        if ($this->_uiHandler) {
            //  Render with UI Render
            $this->_uiHandler->render($this);
        } else {
            //  Executes default BaseList display() method
            //  in C# you'll have base:display()
            parent::display();
        }
    }

}

//**************************************************************
//  Define UI Classe
//**************************************************************

/**
 * Child Specific UI
 */
class UIChildSpecific implements UIBase {

    /**
     *  Overload Render method
     *
     *  Outputs the following
     *      <strong>Elem 1</strong><br/>
     *      <strong>Elem 2</strong><br/>
     *      <strong>Elem 3</strong><br/>
     *
     * @param ChildList $obj
     * @return string
     */
    public function render($obj) {
        //  Output array  for data
        $renderedOutput = array();

        //  Scan through all items in the list
        foreach ($obj->getItems() as $text) {
            //  render item
            $text = "<strong>" . strtoupper(trim($text)) . "</strong>";
            //  Add it to output array
            $renderedOutput[] = $text;
        }

        //  Convert array to string. With elements separated by <br />
        return implode('<br />', $renderedOutput);
    }

}

//**************************************************************
//  Defining Children classes
//**************************************************************

/**
 * Child Class
 */
class ChildList extends BaseListUIExtended {
    // Implement's logic    
}

Testing...

//**************************************************************
//  TESTING
//**************************************************************

//  Test # 1
$plainChild = new ChildList();
$plainChild->add("hairy");
$plainChild->add("girl");
//  Display the object, will use BaseList::display() method
$plainChild->display();
//  Output: hairy girl

//  Test # 2
$uiChild = new ChildList(new UIChildSpecific());
$uiChild->add("hairy");
$uiChild->add("girl");
//  Display the object, will use BaseListUIExtended::display() method
$uiChild->display();
//  Output: <strong>hairy</strong><br /><strong>girl</strong>
Alex
+1 for Hairy Girl
demoncodemonkey
Hi, have now looked at your example a little bit more and it's a nice solution, BUT ... it don't work in more complex solutions. Your solution is elegant when all the items is strings or other value types, but if the objects in the list are complex objects that need even more complex render methods this wont be so nice because there would need to be some sort of "if-is" in the render method in IUIBase implementations, which is what I want to get around.
Marcus
@Marcus, from my understanding of you question you wanted to render objects but did not wanted to keep UI/Design separate. That is exactly what my sample code does. Every class would have it's own UI handler, this will separate UI from Logic (kind of like `view` and `controller` in MVC).Strings were used for simplicity. You can create `Container` and position all objects (buttons, Labels, Textboxes) withing the Container.Either way you will have to code UI/Design logic either in your class (messy code + hard to maintain) or in some kind of UI handler (cleaner code + easier to maintain).
Alex
@Alex: But how do I fill the ChildList with UIChildSpecific implementations without asking each child for its UIChildSpecific implementation. I will end up with the same problem I want to avoid.
Marcus
@Marcus, obviously you are dealing with complex problem. An Idea: add somekind of class prefix to the parent, add method to `getUiSpecific()` to child base class. In the `display()` method, get parent's UI class prefix and create instance of the UI from string. This way all your `UIChildSpecic` will be implemented automatically. You will have to create different UI handlers for different classes. For Example: `parent ui prefix = "FrontEnd"`, you Create `FrontEndUIButton`, `FrontEndUITextBox`, etc. each child in `display` will create instance of UI using parent's pref and child specific props.
Alex
@Alex: Thats a nice solution, but in that case I would rather stick with the IoC-solution above instead of prefix.
Marcus