Visitor pattern might be useful for you. Check dofactory.
Visitor class...
...declares a Visit operation for each class of ConcreteElement in the
object structure. The operation's name and signature identifies the
class that sends the Visit request to the visitor. That lets the
visitor determine the concrete class of the element being visited.
Then the visitor can access the elements directly through its
particular interface
This is similar to Abstract class implementation as told by Vikdor.
Here is a wiki link for this.
The visitor pattern requires a programming language that supports
single dispatch and method overloading.
I have provided a very simple implementation using the visitor pattern for your requirement of different channels and volume settings for WidgetLite and Pro. I have mentioned in comments where the visitor pattern will greatly help you in reducing the if-else calls.
The basic philosophy is that you pass the control (ex. volume) to the widget and it will know how to use it as required. Hence, control object itself has a very simplified implementations. Each widget's code remains together!!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WidgetVisitor
{
//This is the widget interface. It ensures that each widget type
//implements a visit functionality for each control. The visit function
//is overloaded here.
//The appropriate method is called by checking the parameter and this
//avoids the if-then logic elegantly
public interface Widget
{
void visit(Volume vol);
void visit(Channel chan);
void Display(AllControls ac);
}
//This is the interface which defines the controls. Each control that
//inherits this interface needs to define an "accept" method which
//calls the appropriate visit function of the right visitor,
//with the right control parameter passed through its call!
//This is how the double dispatch works.
//Double dispatch: A mechanism that dispatches a function call to different concrete
//functions depending on the runtime types of two objects involved in the call.
public interface WidgetControls
{
void accept(Widget visitor);
}
//I have implemented the volume control and channel control
//Notice how the code for defining each control is the SAME
//in visitor pattern! This is double dispatch in action
public class Volume : WidgetControls
{
public int volLevel { get; set; }
public int volJazz { get; set; }
public int volPop { get; set; }
public void accept(Widget wc)
{
wc.visit(this);
}
}
public class Channel : WidgetControls
{
public int channelsProvided { get; set; }
public int premiumChannels { get; set; }
public void accept(Widget wc)
{
wc.visit(this);
}
}
//Widget lite implementation. Notice the accept control implementation
//in lite and pro.
//Display function is an illustration on an entry point which calls the
//other visit functions. This can be replaced by any suitable function(s)
//of your choice
public class WidgetLite : Widget
{
public void visit(Volume vol)
{
Console.WriteLine("Widget Lite: volume level " + vol.volLevel);
}
public void visit(Channel cha)
{
Console.WriteLine("Widget Lite: Channels provided " + cha.channelsProvided);
}
public void Display(AllControls ac)
{
foreach (var control in ac.controls)
{
control.accept(this);
}
Console.ReadKey(true);
}
}
//Widget pro implementation
public class WidgetPro : Widget
{
public void visit(Volume vol)
{
Console.WriteLine("Widget Pro: rock volume " + vol.volLevel);
Console.WriteLine("Widget Pro: jazz volume " + vol.volJazz);
Console.WriteLine("Widget Pro: jazz volume " + vol.volPop);
}
public void visit(Channel cha)
{
Console.WriteLine("Widget Pro: Channels provided " + cha.channelsProvided);
Console.WriteLine("Widget Pro: Premium Channels provided " + cha.premiumChannels);
}
public void Display(AllControls ac)
{
foreach (var control in ac.controls)
{
control.accept(this);
}
Console.ReadKey(true);
}
}
//This is a public class that holds and defines all the
//controls you want to define or operate on for your widgets
public class AllControls
{
public WidgetControls [] controls { get; set; }
public AllControls(int volTot, int volJazz, int volPop, int channels, int chanPrem)
{
controls = new WidgetControls []
{
new Volume{volLevel = volTot, volJazz = volJazz, volPop = volPop},
new Channel{channelsProvided = channels, premiumChannels = chanPrem}
};
}
}
//finally, main function call
public class Program
{
static void Main(string[] args)
{
AllControls centralControl = new AllControls(3, 4, 2, 5, 10);
WidgetLite wl = new WidgetLite();
WidgetPro wp = new WidgetPro();
wl.Display(centralControl);
wp.Display(centralControl);
}
}
}