tags:

views:

178

answers:

5

How can i make the following class as general as possible (for maximum reuse) without creating too many classes of the same type, albeit with one extra property.

I want to avoid writing 3 slightly different versions of the same class

1# Class with No SubContent

public class Content
    {
         public string PageName { get; set; }
    }

2# Class with Subcontent

public class Content
    {
         public string PageName { get; set; }

         public IList<Content> SubContent {get; set;}    //same as class

    }

3# Class with sub content of another type

public class Content
        {
             public string PageName { get; set; }

             public IList<DetailContent> SubContent {get; set;} //Note the different def

        }

Of course i can create a generic class, but i find this confusing for consumers. It is inferring that the class is of Type T, when in fact its the Property that requires the type

public class Content<T>
            {
                 public string PageName { get; set; }

                 public IList<T> SubContent {get; set;} //Note the different def

            }

Generic Properties are not supported. So are there any patterns or suggestion on how i can handle this problem?

+2  A: 

Perhaps you can have a look at the Composite Design Pattern

Frederik Gheysels
Great! I knew there must be a pattern for this type of problem. but couldn't think of the name or how to google it. I can only describe it!
A: 

Why not make an interface for the content classes:

public interface IContent { public function GetContent() }

and then you can use List in you content class?

you could even make the interface generic

Nicky De Maeyer
A: 

You could perhaps keep your derived classes and use the Factory Method Pattern to which you pass in an enumeration type and return an interface e.g.

public enum ContentType
{
    NoContent,
    SubContent,
    DetailSubContent
}

public class Content: IContent
{
    ....
}

public class SubContent: IContent, ISubContent
{
    ....
}

public class DetailSubContent: IContent, IDetailSubContent
{
   ....
}

Then you could have a factory method to return you whichever type of object you require:

public static class ContentFactory
{
    public static IContent Create(ContentType type)
    {
         switch (type)
         {
             case NoContent:
                 return new Content();
             case SubContent:
                 return new SubContent();
             case DetailSubContent:
                 return new DetailSubContent();
             default:
                 return null;
         }
    }
}

Usage

IDetailSubContent content = ContentFactory.Create(ContentType.DetailSubContent) as IDetailSubContent;

if C# 3.0 or above:

var content = ContentFactory.Create(ContentType.DetailSubContent) as IDetailSubContent;
James
var content = ContentFactory.Create(), content is always inferred to IContent? Even if the factory is creating a DetailSubContent, right?
Chris Richner
Ah my bad, I should have updated the Create method to return the specific interfaces i.e. return new DetailSubContent() as IDetailSubContent.
James
+3  A: 

whats wrong with:

public class Content<T>
    {
        public string PageName { get; set; }

        public IList<T> SubContent { get; set; } //Note the different def

    }

? it works you know...

Sure it does, but if i wanted to create an instance of Content without a type...i.e There are no SubContents. I can't really do this without creating new classes to support this.
+2  A: 

What about

public class Content
{
    public string PageName { get; set; }
}

public class ContentWithSubContent<T> : Content
{
    public IList<T> SubContent { get; set; }
}

and if you want to be able to access SubContent not knowing the actual type, you could use

public class Content
{
    public string PageName { get; set; }
}

public interface IContentWithSubContent
{
    IEnumerable SubContent { get; }
}

public class ContentWithSubContent<T> : Content, IContentWithSubContent
{
    public IList<T> SubContent { get; set; }

    IEnumerable IContentWithSubContent SubContent 
    { 
        get { return this.SubContent; } 
    }
}

that way you can access the SubContent property bypassing generics if you need to, by using IContentsWithSubContent rather than Content.

Ruben
Good suggestion.