0

Suppose we have an Abstract factory that creates for us some products. We know that the abstract factory can provide us some specific subclasses of the product but we don't want to check the type (this is the main reason for this pattern). Now we need to create a specific view for each type of object, how can we do this without know the specific type? Should the same factory create the different views?

Update: I created a github repo to try out all the different approaches.

IgnazioC
  • 4,554
  • 4
  • 33
  • 46
  • Yes. "_Look & Feel [Abstract Factory](http://www.oodesign.com/abstract-factory-pattern.html) is the most common example._" – jaco0646 Aug 27 '15 at 14:03
  • What is a "view" in your case? Is a "view" something that a product should know or care about? Can you have multiple different views for a single product? Can some products have views that other products cannot? Providing more details or some examples would get you much better suggestions. – vgru Aug 28 '15 at 13:01
  • A view is just a rapresentation of the Product. Products shoudn't care about the structure of the view. A product can be displayed using multiple views in the same application. Some product should be displayed in specific kind of views. – IgnazioC Aug 28 '15 at 13:04
  • @DraganBozanovic added a github repo with the explanation of the code kata. – IgnazioC Aug 29 '15 at 15:30

1 Answers1

1

For this problem we can look how abstract factory pattern implemented in ADO.NET.

We have an abstract factory called DbProviderFactory. There are several implementations of this factory like SqlClientFactory, MySqlClientFactory, OracleClientFactory etc.

For this problem, the products are database related objects like connection, command, data adapter etc.

At first our abstract factory gives us a connection (product). It could be MySqlConnection or OracleConnection. The only thing we know is, it is a DbConnection.

DbProviderFactory factory = ...
DbConnection conn = factory.CreateConnection();

Now we need to create a command object (view) that can be used along with this connection.

DbCommand cmd = conn.CreateCommand();

As you see, command (view) is created by the connection (product) not by the abstract factory.

But this is not quite true...

What actually happens is hidden in implementation details. When abstract factory creates the connection it passes itself to the connection. And when we ask for a command, the connection creates the command via the abstract factory provided.

So, if we return to your problem, implementation can be something like this.

interface IView { 
    IProduct Product { get; set; }
    void Render();
}

interface IProduct { 
    IView CreateView();
}

interface IAbstractFactory {
    IProduct CreateProduct();
    IView CreateView();
}

class View1 : IView {
    public IProduct Product { get; set; }

    public void Render() {
        Product1 p1 = (Product1)Product;
        // Do product1 specific rendering here
    }
}

class Product1 : IProduct {
    private IAbstractFactory factory;

    public Product1(IAbstractFactory factory) {
         this.factory = factory;
    }

    public IView CreateView() {
         IView view = factory.CreateView();
         view.Product = this;
         return this;
    }
}

class Factory1 : IAbstractFactory {
    IProduct CreateProduct() {
        return new Product1(this);
    }        

    IView CreateView() {
        return new View1();            
    }        
}
Mehmet Ataş
  • 11,081
  • 6
  • 51
  • 78
  • I really like your answer. Just ad additional clarification...are you ok with the idea to ask a view (so an UI object) directly to the model? – IgnazioC Aug 28 '15 at 09:57
  • 1
    If it is a view model (a model that is designed to be used in views) and object creation (`new` keyword) remain in the abstract factory implementation, that's OK. Actually, abstract factory acts like a [Strategy](https://en.wikipedia.org/wiki/Strategy_pattern) in `Product` – Mehmet Ataş Aug 28 '15 at 10:41
  • 1
    Your example is not what I would recommend. First, `IView.Product` has to be set by a different piece of code. This means you are relying on future developers to know this and make sure they set it. Second, you are casting it to a concrete type. This is again completely against the philosophy of OOP. Both issues will happily pass compilation and then fail at runtime. – vgru Aug 28 '15 at 12:55
  • @Groo what's your suggestion? – IgnazioC Aug 29 '15 at 08:26