views:

196

answers:

5

I wrote this example to help explain. As you can see I have an object hierarchy. I'd like to modify the GetFeatures() function to only return the features added by constructor of object type I instantiated. For example, BasicModel.GetFeatures(new LuxuryModel()) should only return the features "Leather Seats" and "Sunroof". I don't mind using reflection if I have to.

Public Class Feature

 Public Sub New(ByVal model As BasicModel, ByVal description As String)
  _model = model
  _description = description
 End Sub

 Private _model As BasicModel
 Public Property Model() As BasicModel
  Get
   Return _model
  End Get
  Set(ByVal value As BasicModel)
   _model = value
  End Set
 End Property

 Private _description As String
 Public Property Description() As String
  Get
   Return _description
  End Get
  Set(ByVal value As String)
   _description = value
  End Set
 End Property

End Class


Public Class BasicModel
 Public Sub New()
  _features = New List(Of Feature)
 End Sub

 Private _features As List(Of Feature)

 Public ReadOnly Property Features() As List(Of Feature)
  Get
   Return _features
  End Get
 End Property

 Public Shared Function GetFeatures(ByVal model As BasicModel) As List(Of Feature)
  'I know this is wrong, but something like this...'
  Return model.Features.FindAll(Function(f) f.Model.GetType() Is model.GetType())
 End Function
End Class


Public Class SedanModel
 Inherits BasicModel

 Public Sub New()
  MyBase.New()
  Features.Add(New Feature(Me, "Fuzzy Dice"))
  Features.Add(New Feature(Me, "Tree Air Freshener"))
 End Sub
End Class


Public Class LuxuryModel
 Inherits SedanModel

 Public Sub New()
  MyBase.New()
  Features.Add(New Feature(Me, "Leather Seats"))
  Features.Add(New Feature(Me, "Sunroof"))
 End Sub
End Class
A: 

If you want to keep your current hierarchy/properties, just create an instance of the base class and subtract the Features in the base class from the features in the derived class.

Alternatively, you might want to consider changing your class hierarchy a bit to make this easier on yourself.

For instance, you could split the Features property into two properties, say ModelFeatures and AllFeatures.

ModelFeatures is specific to the current model ("Leather Seats" and "Sunroof" for LuxuryModel, etc.) AllFeatures returns the union of MyBase.AllFeatures and ModelFeatures. This makes getting the current Model's features a trivial property access.

P.S. Please forgive my VB errors, C# is my preferred lang.

micahtan
A: 

I am not real familiar with VB.NET syntax, so here is C# syntax that should work:

public IList<Function> GetFeatures(BasicModel model)
{
    return model.Features.Where(f => f.Model.GetType() == model.GetType()).ToList();
}
jrista
In VB this doesn't filter anything. All features are still returned.
adam0101
It shouldn't matter whether its done in VB or C#. If you use a Where clause like that, and the types are indeed different (note that doing GetType will not compare the base types of the two, only the exact type of each instance), then you should get a filtered list. If it is "not working", then the only option is that every feature on the given model is for the model your passing in.
jrista
A: 

(based on your sample code above)

To be honest I don't think this belongs in your Car Heirachy, it seems to me that you are wanting to get at static data and using your base class as the container for it with your decendant types as a key. As you can see this leads to some clumsy or overly complex coding for something simple.

I would be much more inclined to put your static data where it belongs, (in this case) with the Feature class itself. I am not a VB guy so this code snippet in C#.

So in the Feature class itself, have a static property (or method)

public static IEnumerable<Feature> GetLuxuryFeatureSets
{
  get
  {
    yield return new Feature() { Name = "Leather Seats" };
    yield return new Feature() { Name = "Sunroof" };
  }
}

And repeat for your other models. So now you have a container for your static data that makes sense, the class itself.

Tim Jarvis
A: 

Since there is no way to distinguish one feature from another, you need to be able to ask a Model what features it has added. Rather than add features to the list in your constructor, you should have a method that returns a list of features that particular model adds ...

public class LuxuryModel
{
  public LuxuryModel
  {
     Features.Add( GetFeatures() );
  }
  public List<Features> GetFeatures()
  {
     return new List<Features>( /* new up leather and whatever */
  }
}

Then, GetFeatures could do the downcast and ask the instance it is given for the list of features that that particular instance adds ...

public List<Features> GetFeatures<T>( BasicModel model )
{
   var m = Model as T;
   return m.GetFeatures();
}
JP Alioto
A: 

Another way to do this if you don't want this data to be static (see my other answer), and you wnat it to be extendable to new classes, and for those classes to control the feature sets. Then use polymorphism (again C#, as I am no VB guy)

In your base class create a virtual method that returns features, in your decenent classes override the property, polymorphism allows your variables as type basicmodel as long as they were constructed as the descendant type to return the correct list.

  public class BasicModel
  {
    public virtual IEnumerable<Feature> GetFeatures 
    {
      get
      {
        throw new NotImplementedException();
      }
    }
  }


  public class LuxuryModel :BasicModel
  {
    public override IEnumerable<Feature> GetFeatures
    {
      get
      {
        yield return new Feature() { Name = "Leather Seats" };
        yield return new Feature() { Name = "Sunroof" };
      }
    }
  }



private void button1_Click(object sender, EventArgs e)
{
  StringBuilder sb = new StringBuilder();

  BasicModel bm = new LuxuryModel();

  foreach (Feature f in bm.GetFeatures)
  {
    sb.AppendLine(f.Name);
  }
  MessageBox.Show(sb.ToString());
}
Tim Jarvis