Search code examples
oopdesign-patternsui-patterns

Design pattern for create specific view elements


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.


Solution

  • 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();            
        }        
    }