views:

574

answers:

5

The code below consists of two classes:

  • SmartForm (simple model class)
  • SmartForms (plural class that contains a collection of SmartForm objects)

I want to be able to instantiate both singular and plural classes like this (i.e. I don't want a factory method GetSmartForm()):

SmartForms smartForms = new SmartForms("all");
SmartForm smartForm = new SmartForm("id = 34");

To consolidate logic, only the plural class should access the database. The singular class, when asked to instantiate itself, will simply instantiate a plural class, then pick the one object out of the the plural object's collection and become that object.

How do I do that? I tried to assign the object to this which doesn't work.

using System.Collections.Generic;

namespace TestFactory234
{
    public class Program
    {
        static void Main(string[] args)
        {
            SmartForms smartForms = new SmartForms("all");
            SmartForm smartForm = new SmartForm("id = 34");
        }
    }

    public class SmartForm
    {
        private string _loadCode;

        public string IdCode { get; set; }
        public string Title { get; set; }

        public SmartForm() {}

        public SmartForm(string loadCode)
        {
            _loadCode = loadCode;
            SmartForms smartForms = new SmartForms(_loadCode);
            //this = smartForms.Collection[0]; //PSEUDO-CODE
        }

    }

    public class SmartForms
    {
        private string _loadCode;

        public List<SmartForm> _collection = new List<SmartForm>();
        public List<SmartForm> Collection
        {
            get
            {
                return _collection;
            }
        }

        public SmartForms(string loadCode)
        {
            _loadCode = loadCode;
            Load();
        }

        //fills internal collection from data source, based on "load code"
        private void Load()
        {
            switch (_loadCode)
            {
                case "all":
                    SmartForm smartFormA = new SmartForm { IdCode = "customerMain", Title = "Customer Main" };
                    SmartForm smartFormB = new SmartForm { IdCode = "customerMain2", Title = "Customer Main2" };
                    SmartForm smartFormC = new SmartForm { IdCode = "customerMain3", Title = "Customer Main3" };
                    _collection.Add(smartFormA);
                    _collection.Add(smartFormB);
                    _collection.Add(smartFormC);
                    break;
                case "id = 34":
                    SmartForm smartForm2 = new SmartForm { IdCode = "customerMain2", Title = "Customer Main2" };
                    _collection.Add(smartForm2);
                    break;
                default:
                    break;
            }
        }
    }
}
+2  A: 

You could use an internal or protected modifier for the constructor, instead of public. Such that:

public SmartForm() {}
public SmartForm(string loadCode)
{
    _loadCode = loadCode;
    SmartForms smartForms = new SmartForms(_loadCode);
    //this = smartForms.Collection[0]; //PSEUDO-CODE
}

becomes:

internal SmartForm() {}
internal SmartForm(string loadCode)
{
    _loadCode = loadCode;
    SmartForms smartForms = new SmartForms(_loadCode);
    //this = smartForms.Collection[0]; //PSEUDO-CODE
}
Lucas Jones
+6  A: 

You can't get one object to "become" another.

Use static methods instead of constructors (and make the constructors private/internal/whatever so that only the static methods can access them). There are numerous benefits to using static methods over constructors:

  • You can return null if appropriate
  • You can return an existing object if appropriate
  • You can do plenty of work and then call a simple constructor which just sets fields

The downside is they don't work with C# collection/object initializers :(

An alternative to static methods (which don't work well with dependency injection, admittedly) is to have a separate factory and call instance methods on that.

Jon Skeet
+3  A: 

You cannot. Microsoft does this by defining a public static Create() method instead. See for example WebRequest and HttpWebRequest in the System.Net namespace. I suggest you do the same.

Make the base class' constructor internal or private, so it cannot be directly instantiated. Or better yet, make it an abstract class. Make use of the hidden constructors in your Create() implementation.

Thorarin
+4  A: 

It doesn't have to be exactly the same object, it just has to appear to be the same to external observers. If you copy all the data from one object to another, it will accomplish the same thing.

public SmartForm(string loadCode)
{
    _loadCode = loadCode;
    SmartForms smartForms = new SmartForms(_loadCode);
    this.IdCode = smartForms[0].IdCode;
    this.Title = smartForms[0].Title;
}
Tadmas
Exactly, I'm looking for something like that, all I need are the simple properties, but it needs to be dynamic for any object type, I'm thinking of some kind of "cloning via reflection" class, isn't there something like that, e.g. System.Reflection.Clone(smartForms[0])?
Edward Tanguay
A: 

You could make a private class called SmartFormData, and put all of the values into it.

Make the SmartForms plural class maintain a collection of those objects, and make the SmartForm singular constructor find the right SmartFormData instance in SmartForms plural.

Then, make all of the properties in SmartForm singular fetch their values from SmartFormData

However, the best way to do it is to have a private constructor and use a factory method.

Is there a reason that you have to use a constructor?

SLaks