tags:

views:

162

answers:

2

(Edited a lot) I've got some classes with Abstracts Members. The concrete type of the abstract members is to be determined at the class instanciation, based on the user's input. However, the second member's concrete type might depend on the first member.

I'm trying to do something keeping the MVP design pattern in mind. I taught about making the Presenter pass a delegate to the Model's Ctor, which he (the Ctor) would use to request the informations needed for the instanciation of the class. I'm not sure if it's a good idea. Here is what I wrote :

// In the Model :
public class Model
{
    public E Element1;
    public E Element2;

    public Model(CustomCtor<ModelElement, IModelElement> GetModelElement)
    {
        this.Element1 = (E)GetModelElement(ModelElement.E, null);
        this.Element2 = (E)GetModelElement(ModelElement.E, null);
        //Element2 does not depend on Element1 in this case though.
    }
}

public abstract class E : IModelElement { }

public class EA : E
{
    public string Element1;
    public EA(string Element1) { this.Element1 = Element1; }
}

public class EB : E
{
    public int Element1;
    public EB(int Element1) { this.Element1 = Element1; }
}

public interface IModelElement { }

public enum ModelElement { E, EA, EB }

// In the Presenter :
public class Presenter
{
    View.View view1;

    public Presenter() { }
    public void SetView(View.View view) { this.view1 = view; }

    public Model.Model MakeModel()
    {
        CustomCtor<ModelElement, IModelElement> GetModelElement = new CustomCtor<ModelElement, IModelElement>(GetModelElement<ModelElement, IModelElement>);
        return new Model.Model(GetModelElement);
    }

    private Model.IModelElement GetModelElement<ModelElement, Tout>(Model.ModelElement ME, object obj)
    {
        switch (ME)
        {
            case Model.ModelElement.E:
                return MakeE();
            // One case per Model.ModelElement
            default:
                throw new Exception("ModelElement not implemented in the Presenter.");
        }
        return default(Model.IModelElement);
    }

    private E MakeE()
    {
        switch (view1.AskEType())
        {
            case 1:
                return MakeEA();
            case 2:
                return MakeEB();
            default:
                throw new Exception();
        }
    }

    private EA MakeEA() { return new EA(view1.AskString("EA.Element1 (String)")); }
    private EB MakeEB() { return new EB(view1.AskInt("EB.Element1 (Int)")); }
}

// Shared to the Model and the Presenter :
public delegate TOut CustomCtor<EnumType, TOut>(EnumType Enum, object Params) where EnumType : struct;

// In the View :
public class View
{
    public int AskEType()
    {
        Console.WriteLine(string.Format("Type of E : EA(1) or EB(2)?"));
        return int.Parse(Console.ReadLine());
    }
    public string AskString(string Name)
    {
        Console.Write(string.Format("{0} ? ", Name));
        return Console.ReadLine();
    }
    public int AskInt(string Name)
    {
        Console.Write(string.Format("{0} ? ", Name));
        return int.Parse(Console.ReadLine());
    }
}

//In the Program :
class Program
{
    static void Main(string[] args)
    {
        View.View view1 = new View.View();
        Presenter.Presenter presenter1 = new Presenter.Presenter();

        presenter1.SetView(view1);
        presenter1.MakeModel();
    }
}

Does that make sense? Is there a name for the thing I'm trying to do? (beside "A weird thing") Are you aware of a design pattern I should read on? I taught about mixing the Builder design pattern with the MVP, but I'm not sure how I'd do that.

Thanks

A: 

I am not certain this is what your asking about, but I am assuming you are trying to keep your view isolated from your model. If that is indeed what you are trying to do, I think your taking a much too complicated approach. The view is simply a presentation and feedback medium. It really does not need to know anything about models, it can be designed to make use of simple data in a property bag of some kind. This creates a cleaner separation, however, it often makes rendering data and maintaining the view a lot harder as well.

First question I would ask is, is it REALLY worth it to expend so much effort keeping your view entirely isolated from your model? What are you really gaining by having an absolute separation?

If you do indeed need a separation, make sure you understand the roles of view and presenter. The view is dumb...it knows nothing and does nothing. It presents information and forms. The browser issues commands requested by the user. The presenter handles commands, and directs data to its view. The concept of "presenter asking the view" for anything is generally incorrect. The presenter should be handling the command (http request) directly, so it should know any and all details about a particular command. When it comes time to render the view, the presenter should provide any data to the view in whatever form the view needs it to be in. If you do not want your view to know about your object model, then either create properties on the view itself to contain the data, or create a view-specific model that encapsulates the data required.

EDIT:

I've just read your update. I think I understand your problem a bit better now. First off, before I go any farther, you need to reorganize responsibilities a little bit. Currently, you have it such that your view is responsible for handling input. That is a bit of a corruption of the purpose and concept of a 'view'. In both MVP and MVC, the view is supposed to be as "dumb" as possible, and really should not be responsible for processing anything...commands, actions, input, etc. should all be the responsibility of the Controller or Presenter.

Seeing that your view is actually a console application, not a web forms application (which was my original assumption), I think that MVC might actually be a better fit for your needs. MVP is a good solution for getting around the deficiencies of ASP.NET WebForms, but it is not as powerful or successful at helping separate concerns as MVC is. I would look into implementing an MVC pattern, which was originally designed for console type applications. The controller becomes the central input handler, which then issues commands to command handlers and your model. The view would then be pure and true to form...only rendering information and nothing else.

If there is some reason why you cannot use an MVC approach, which I think would be ideal, and must use MVP, I can offer more advice on how you could fix your current implementation. However, I would strongly suggest looking into using MVC, as that patterns was originally designed to solve the very problem you are trying to solve...in console applications.

jrista
I wrote this code with the Viewer being a Console, but in fact I plan to use an "IView" because the real views will be WinForm and WebForm. Also, the inputs will be a bit more complex (dropdown and Numeric textbox mainly) so I planned to make some User Controls for each component types. I also planned to make the Presenter read the information directly from the view (and not make the view return values). Given that additional information, would you still suggest me to go with the MVC instead of the MCP? Thanks for your input, it's really appreciated.
Tipx
With WebForms, MVP can be helpful. It is difficult to create an MVC for WebForms as it is. An MVP can also work with Windows Forms, as WinForms and WebForms work very similarly. However, that does not really change the picture much. It is very important that the view be "dumb", and that the presenter handle input. The presenter should never have to ask the view for anything...as input should be routed through the presenter first, not the other way around. An IView implementation should give the presenter the ability to push data to the view, but not much else.
jrista
Thanks, I'll keep that in mind while I'll try some things. I have a next to none formation in programming and I haven't seen much design patterns, but I have no doubt your advices will spare me some traps! (even so, I'll make errors, but will learn from them.) Thanks.
Tipx
No problem. If you need more help, feel free to contact me. I've written many a home-grown MVP myself. Most of my work is web based, so I use ASP.NET MVC (sometimes Castle Monorail) almost exclusively these days, however my MVP skills are still strong.
jrista
A: 

From what I can tell, the Builder method should be encapsulated in your presenter since he is allowed to know the model.

public class Presenter
{
    public BaseClassDTO Build(string name)
    {
        BaseClass1 bc = ModelController.Instance.BuildFancy(name);
        BaseClassDTO dto = Mapper.Transform(bc);
        return dto;
    }
}

As you can see, you might need DTOs (Data Transfer Objects) to carry your data up to the View layer. To convert BOs (Business Objects) into DTOs, a Mapper is often a good way to encapsulate this behavior. In your mapper, you could either convert your roughly-typed objects using type-safe compile-time conversion:

if (obj is BaseClass1) // Convert it this way...
if (obj is BaseClass2) // Convert it that way...

or using reflection:

dto.DataArray = obj.GetType().GetProperties();

Either way, your DTO will need to be general in the way it carries its data. A common mistake is to duplicate the class hierarchy in DTOs; it often is unnecessary and lead to multiple maintainability issues. Note that it might be necessary or convenient at times.

So, relevant patterns:

Bryan Menard
I just rewrote my question saying what I've actually done. I think it's related to what you said in your answer. I'll look into that later on. Thanks
Tipx