Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

polymorphism and n-tier applications

I have this doubt for a long time... hope anyone can enlight me.

Suppose I have 3 classes in my model.

abstract class Document {}
class Letter extends Document {}
class Email extends Document {}

and a service class with a method that returns a Document (either a Letter or Email).

class MyService {
    public Document getDoc(){...}
}

So in my controller I want to display the Document returned by MyService, and I want it to be displayed using a view for the Email and other for the Letter. How could a controller know which document view invoke? the letterView or the emailView?.

Often I make an if statement on the controller to check the type of the Document received by the service tier... however I dont think it is the best approach from the OOP point of view, also if I implement a few boolean methods Document.isLetter(), Document.isEmail() the solution is, in essence, the same.

Another thing is to delegate the view selection to the Document somehow. something like:

class MyController {
    public View handleSomething() {
        Document document = myService.getDocument();
        return document.getView();
    }
}

But, omg, why my model objects must know anything about the view?

Any toughts are appreciated :)

like image 630
Mauricio Avatar asked Feb 16 '10 20:02

Mauricio


2 Answers

This is a great question. There is more than one plausible approach here; you have to balance the trade-offs and make a choice that suits your circumstances.

(1) Some will argue that that Document interface should provide a method for instances to render themselves. This is attractive from an OO standpoint, but depending on your view technologies, it may be impractical or downright ugly to load your concrete Document classes -- which are probably simple domain model classes -- with knowledge of JSPs, Swing Components, or whatever.

(2) Some will suggest putting perhaps a String getViewName() method on Document that returns, for example, the path to a JSP file that can properly render that document type. This avoids the ugliness of #1 at one level (library dependencies/"heavy lifting" code), but conceptually poses the same problem: your domain model knows that it's being rendered by JSPs and it knows the structure of your webapp.

(3) Despite these points, it's better if your Controller class doesn't know what types of documents exist in the universe and which type each instance of Document belongs to. Consider setting up some sort of view-mapping in some sort of text-based file: either .properties or .xml. Do you use Spring? Spring DI can help you quickly specify a Map of concrete Document classes and the JSP/view components that render them, then pass it to a setter/constructor of you Controller class. This approach allows both: (1) your Controller code to remain agnostic of Document types and (2) your domain model to remain simple and agnostic of view technologies. It comes at the cost of incremental configuration: either .properties or .xml.

I'd go for #3 or -- if my budget (in time) for working on this problem is small -- I'd (4) simply hard-code some basic knowledge of Document types in my Controler (as you say you're doing now) with a view toward switching to #3 in the future the next time I'm forced to update my Controller due to less-than-optimal OO characteristics. The fact is that #s 1-3 each take longer and are more complex than #4, even if they are "more correct." Sticking with #4 is also a nod to the YAGNI Principal: there's no certainty you'll ever experience the negative effects of #4, does it make sense to pay the cost of avoiding them up-front?

like image 152
Drew Wills Avatar answered Sep 21 '22 14:09

Drew Wills


Your controller shouldn't know. It should ask the Document to display itself, and the Document can do this, or provide sufficient information to let the View handle this polymorphically.

Imagine if at a later stage you add a new Document type (say, Spreadsheet). You really only want to add the Spreadsheet object (inheriting from Document) and have everything work. Consequently the Spreadsheet needs to provide the capability to display itself.

Perhaps it can do it standalone. e.g.

new Spreadsheet().display();

Perhaps it can do it in conjunction with the View e.g. a double-dispatch mechanism

new Spreadsheet().display(view);

In either case, the Spreadsheet/Letter/Email would all implement this view() method and be responsible for the display. Your objects should talk in some view-agnostic language. e.g. your document says "display this in bold". Your view can then interpret it according to its type. Should your object know about the view ? Perhaps it needs to know capabilities that that view has, but it should be able to talk in this agnostic fashion without knowing the view details.

like image 42
Brian Agnew Avatar answered Sep 18 '22 14:09

Brian Agnew