views:

188

answers:

5

Alright, so as you probably know, static inheritance is impossible in C#. I understand that, however I'm stuck with the development of my program.

I will try to make it as simple as possible. Lets say our code needs to manage objects that are presenting aircrafts in some airport. The requirements are as follows:

  • There are members and methods that are shared for all aircrafts

  • There are many types of aircrafts, each type may have its own extra methods and members. There can be many instances for each aircraft type.

  • Every aircraft type must have a friendly name for this type, and more details about this type. For example a class named F16 will have a static member FriendlyName with the value of "Lockheed Martin F-16 Fighting Falcon".

  • Other programmers should be able to add more aircrafts, although they must be enforced to create the same static details about the types of the aircrafts.

  • In some GUI, there should be a way to let the user see the list of available types (with the details such as FriendlyName) and add or remove instances of the aircrafts, saved, lets say, to some XML file.

So, basically, if I could enforce inherited classes to implement static members and methods, I would enforce the aircraft types to have static members such as FriendlyName. Sadly I cannot do that.

So, what would be the best design for this scenario?

+3  A: 

Don't use static methods. use instance methods instead.

Also the top abstract may expose an abstract method that will return the aircraft specific name.

public abstract class Aircraft
{
    public abstract string Name { get; }
    public abstract string FriendlyName { get; }
}
Mendy
Generally, yes, but he does clearly explain the use case in his question - he needs to be able to provide a list of available types without actually creating them first. Very common in plugin-like architectures. Creating an instance might be expensive, and if there are 100 different types, it doesn't make sense to create them all just to get a description for each.
Aaronaught
@Aaronaught - to list he can use attributes as you suggested, and to instantiate - either code generation or have hashtable where key = Type and Value = Delegate to constructor. Running delegate is cheaper then Reflection.
Vitaly
Couldn't explain it better, thanks Aaronaught (and to Mendy and David for their answers)
yellowblood
I'm going to use reflection anyway, I kinda forgot to say that new types of aircraft will be included in the software as DLLs, which I will have to parse and present in the GUI.
yellowblood
Could you separate instantiation from construction? If you had a method 'initialize' that was distinct from building the actual object, then you could do use abstraction to enforce your description requirements.
mmr
+8  A: 

One answer is to decorate each class with attributes (metadata):

[Description("Lockheed Martin F-16 Fighting Falcon")]
public class F16 : Aircraft
{
    // ...
}

This is using the DescriptionAttribute already in System.ComponentModel.

You can get the metadata like this:

Type t = typeof(F16);
DescriptionAttribute attr = (DescriptionAttribute)Attribute.GetCustomAttribute(t,
    typeof(DescriptionAttribute));
string description = (attr != null) ? attr.Description : t.Name;

This will get you the description text from a reference to the F16 class.

Aaronaught
Note: If you find yourself doing this type of thing frequently, you can create an extension method that will allow you to just write `typeof(F16).GetDescription()`.
Aaronaught
Thanks, this look like a good way, but you can't really enforce it, am I right?I mean, a developer could create an aircraft type that does not have this attribute - and I really wanted to find a way to enforce it.
yellowblood
@yellowblood: Yes, a developer could create a type without the attribute. If it were theoretically possible to have overridable static methods, a developer could also choose to return an empty string or `null` from it or even throw an exception. Either way, you want to code defensively and use good defaults - in my example I've defaulted to using the class name if a description is not available. I do understand that there's a semantic difference, but unfortunately you cannot force attributes to be defined. What you *could* do is create an FxCop rule that would identify the missing ones.
Aaronaught
Yet again thanks a lot for your help. I will use attributes and at least one custom attribute which the developers will have to write in addition of the aircraft type (I will supply the base-class attribute of course). I just read about FxCop and I don't think I will need it. I will just write a simple validator for the attributes.Case closed I guess, thank you!
yellowblood
+4  A: 

Why do you need these properties to be static?

public class Aircraft
{
protected string AircraftName { get; protected set; }
}

public class F16 : Aircraft
{
   public F16()
   {
     AircraftName="F16 Falcon";
   }
}
David Lively
+1  A: 

Use an enumeration for the friendly names, and create an instance member of that type for the friendly name. Require the initialization of this member during construction.

Dustman
+2  A: 

This is a case where you may benefit from a Factory pattern. Instead of importing specific types of Aircraft, provide a standard IAircraftFactory interface that defines what every Aircraft Factory needs to do for you. This is where you can return descriptions, UI information, etc. The Aircraft Factory is then responsible for creating the particular Aircraft. Because your clients must create a custom Factory in order to expose their Aircraft, they are forced to implement the interface and reminded (via its members) that they have a contract to fulfill.

Something like:

public interface IAircraft
{
    //Aircraft instance details...
}

public interface IAircraftFactory
{
    //Can include parameters if needed...
    IAircraft BuildAircraft();

    //And other useful meta-data...
    string GetDescription();
}

//In some other Client-provided DLL...
public class MyAircraftFactory : IAircraftFactory
{
    IAircraft BuildAircraft()
    {
        return new MyAircraft();
    }

    //...
}
Dan Bryant
Thank you for the answer. Although Factory Pattern will help me split the meta-data from the instance details, I will still have to create an instance (of MyAircraftFactory) in order to read the meta-data. Assuming I'm already using reflection to read the DLL, shouldn't I avoid it (creating an instance for each type)?
yellowblood
The idea here is that you only ever create one Factory instance for each type of aircraft. You only have to use Reflection to find the factory. Incidentally, this fits very naturally with extension systems like MEF, where you can find all implementations of a specific interface and instantiate them auto-magically.
Dan Bryant
Another advantage of this approach is that your clients have more control over the construction process of the Aircraft, giving them the flexibility to do things behind the scenes, like perhaps cache subsystems that multiple Aircraft use, without having to rely on static state. Any shared state can be nicely encapsulated in the factory or its members.
Dan Bryant
Come thinking of it, if I use the attributes solution I will end up creating instances too - after all attributes ARE instances. The Factory solution gives me more control over the clients, as it is simple as inheritance. Would love to see Aaronaught's reply :)
yellowblood
I actually prefer Attributes if the meta-data is simple (like a description or display name), but you mentioned a more complex requirement involving UI layout. This is crossing into the realm of asking the client to _do_ something, which is beyond what Attributes were really intended to perform.
Dan Bryant
This is very nice but I'm don't see how it's relevant to the specific problem of listing the 'descriptions' for the various types of Aircraft?
Kirk Broadhurst
@Kirk, it was a requirement snuck in as a follow-up comment. Must have been scope creep. :)
Dan Bryant
@yellowblood: I considered mentioning abstract factories (as well as MEF) but ultimately decided that it was best left out; the question was about how to label as opposed to instantiate the types. Having said that, if your `Aircraft` objects are expensive to create, or you need deferred loading or the ability to pass constructor parameters to the `Aircraft`, then a set of abstract factories would help. They will also add a lot of overhead to your dev and maintenance time, so weigh the good against the bad.
Aaronaught
Also: Yes, attributes are "instances", but they are very cheap to create and don't require the consumer to actually know how to construct an instance of the type they decorate. Don't consider it the same thing as creating a factory or `Aircraft` instance. MEF is actually built around a combination of attribute metadata and inheritance. Summary: I agree with most of what Dan's said here and consider it independent of my own answer (you could do both if you wanted).
Aaronaught
Alright, I think that's it. Thanks again, you guys really helped me. I haven't come to a final decision yet, I will look into it and decide later. Thank you!
yellowblood