I have a class "Weapon" which has different behavior depending on its classification. The behavior can be changed at runtime, and the behavior can be replaced with others. (For those who know, I am describing a weapon of Worms: Armageddon).
Normally I would create different sub classes of "Weapon" which implement the specific behavior, but since it can be changed at runtime, I can't use such a "static typification".
I thought of a class weapon like the following (C#).
public class Weapon
{
public AirstrikeSettings _airstrikeSettings;
public MineSettings _mineSettings;
public LauncherSettings _launcherSettings,
}
The *Settings classes contain the parameters which define the exact behavior.
At runtime, it must be checked which of the 3 setting instances is not null, to find out what classification of weapon this is. If the classification is changed, for example from Mine to Airstrike, MineSettings are set to null and AirstrikeSettings are initialized.
Is that a correct way to design this case or do I have extreme design problems?
PS: If you are interested, this is the structure I want to reflect in my class: http://worms2d.info/Project_X/Weapon_file_block
As you already said, an inheritance based approach on the level of the Weapon will not allow you to change the behavior at runtime.
I'd suggest to have a look at the Strategy design pattern. It allows you to change the behavior of a class at runtime and you can avoid having lots of if-statements in the class itself.
So in your sample the Weapon class would be the context and the settings the Strategy. Instead of having three separate members for each type of settings, you'd have only one member for the current settings. If you make sure that this is always set, you don't have to check against null and you always call the current setting if you want to execute setting-specific behavior. In order for this to work, the settings need to be based upon a common structure.
In order to be able to set the behavior from the outside, you create a property for the current setting that is accessible from the outside of the class and thus allows a caller to change the setting at runtime.
So your sample would look similar to this:
// base strategy, can also be an abstract class if you want to share
logic between the settings
public interface IWeaponSettings
{
// Definition of common structure for the behaviors
void BehaveInSpecialWay();
// ...
}
public class AirstrikeSettings: IWeaponSettings
{
// Implementation for Airstrike
public void BehaveInSpecialWay()
{
// Airstrike
}
}
public class MineSettings : IWeaponSettings
{
// Implementation for Mining
public void BehaveInSpecialWay()
{
// Mining
}
}
// ...
public class Weapon
{
// Constructor that takes the initial settings as an input
public Weapon(IWeaponSettings settings)
{
Settings = settings
}
// Public property that can be used to change behavior.
// You might want to check against null in the setter
public IWeaponSettings Settings { get; set; }
public void DoSomething()
{
Settings.BehaveInSpecialWay();
}
}
Please note that if some settings do not support some behaviors, they still need to implement them, but simply do nothing or return a default value.
I suggest you create an abstract Weapon class, and three derived classes: AirstrikeWeapon, MineWeapon and LauncherWeapon. Each of these subclasses can have its own settings. Then, add an Attack method to the base class, and implement it using whatever logic you need in each derived class. Then, at runtime, each worm has a Weapon member which can be set to an instance of the weapon it's currently using.
This is called polymorphism, by the way.
Speaking simply, if I have to check null for knowing the type of a class at runtime, then what on earth the Inheritance is doing.
In your case, you can define an abstract class Settings which'll be the parent class of those 3 classes. Then class Weapon will only have the reference of class Settings. Please see the following code:
public abstract class Settings
{
}
public class AirstrikeSettings : Settings
{
}
public class MineSettings : Settings
{
}
public class LauncherSettings : Settings
{
}
public class Weapon
{
public Settings Settings { get; set; } // as this member is public, it can be declared as property
}
Related
Hi fellow game developers, I'm working on a Unity project that allows level designer to edit instructions to scene elements of how they should act to events.
screenshot of command editor in unity inspector
I've managed to express all executable instruction units--expressions, statements, control blocks--with a common abstract base class Command. It comes like this:
[Serializable]
abstract class Command {
public abstract object Execute();
public abstract void Inspect(/* ... */);
}
class CommandCarrier : MonoBehaviour {
public Command command;
}
/*
There are several carrier classes in the real project,
this one is only for illustrating the problem.
Command.Inspect() would be called by a CustomEditor of CommandCarrier.
*/
Where Execute() is to perform the command at runtime, and Inspect() is to draw the inspector GUIs.
Every solid type of command would be a derived class of Command, e.g. an if-else block would be like:
[Serializable]
class Conditional : Command {
public Command condition, trueBranch, falseBranch;
public override object Execute() {
if((bool)condition.Execute()) trueBranch.Execute();
else falseBranch.Execute();
return null;
}
public override void Inspect(/* ... */) { /* ... */ }
}
A constant expression would contain no sub-commands:
[Serializable]
class Constant<T> : Command {
public T value = default(T);
public override object Execute() => value;
public override void Inspect(/* ... */) { /* ... */ }
}
Here comes the problem: all the commands I've written in the inspector panel would be lost as long as a reserialization is triggered (like when the code changed and therefore is recompiled).
This is probably because Unity failed to serialize a subclass instance stored in a field of base class; all the type information and the contained data are lost during reserialization.
What's worse is that these polymorphical instances are even nested.
I've tried to solve the case and failed: given a field of base class, it's apparently impossible to "upgrade" an instance to a subclass by calling whatever methods belonging to that instance; it must be done externally by assigning the field with a subclass instance created elsewhere.
But again, every subclasses have their own fields, and these data I haven't figure out where to recover from.
Could anybody help?
Now that you corrected your code here I would point you to Script Serialization and in particular the section
No support for polymorphism
If you have a public Animal[] animals and you put in an instance of a Dog, a Cat and a Giraffe, after serialization, you have three instances of Animal.
One way to deal with this limitation is to realize that it only applies to custom classes, which get serialized inline. References to other UnityEngine.Objects get serialized as actual references, and for those, polymorphism does actually work. You would make a ScriptableObject derived class or another MonoBehaviour derived class, and reference that. The downside of this is that you need to store that Monobehaviour or scriptable object somewhere, and that you cannot serialize it inline efficiently.
The reason for these limitations is that one of the core foundations of the serialization system is that the layout of the datastream for an object is known ahead of time; it depends on the types of the fields of the class, rather than what happens to be stored inside the fields.
So in your case I would simply use ScriptableObject and do
abstract class Command : ScriptableObject
{
public abstract object Execute();
public abstract void Inspect(/* ... */);
}
and
[CreateAssetMenu]
public class Conditional : Command
{
public Command condition, trueBranch, falseBranch;
public override object Execute() {
if((bool)condition.Execute()) trueBranch.Execute();
else falseBranch.Execute();
return null;
}
public override void Inspect(/* ... */) { /* ... */ }
}
and
public abstract class Constant<T> : Command
{
public T value = default(T);
public override object Execute() => value;
public override void Inspect(/* ... */) { /* ... */ }
}
and e.g.
[CreateAssetMenu]
public class IntConstant : Constant<int>
{
}
each in their own script files with matching name (that part is very important for the serializer).
And then you would create instance of these via the Assets -> right click -> Create -> "Conditional" for example and reference it into the according slots.
Also note that these are now re-usable and you can simply reference the same item in various places, something that wasn't possible if you use a normal serializable class due to
When might the serializer behave unexpectedly?
Custom classes behave like structs
With custom classes that are not derived from UnityEngine.Object Unity serializes them inline by value, similar to the way it serializes structs. If you store a reference to an instance of a custom class in several different fields, they become separate objects when serialized. Then, when Unity deserializes the fields, they contain different distinct objects with identical data.
When you need to serialize a complex object graph with references, do not let Unity automatically serialize the objects. Instead, use ISerializationCallbackReceiver to serialize them manually. This prevents Unity from creating multiple objects from object references. For more information, see documentation on ISerializationCallbackReceiver.
This is only true for custom classes. Unity serializes custom classes “inline” because their data becomes part of the complete serialization data for the MonoBehaviour or ScriptableObject they are used in. When fields reference something that is a UnityEngine.Object-derived class, such as public Camera myCamera, Unity serializes an actual reference to the camera UnityEngine.Object. The same occurs in instances of scripts if they are derived from MonoBehaviour or ScriptableObject, which are both derived from UnityEngine.Object.
thanks in advance for reading this. I don’t fully understand how/when to use abstracts so I am trying to think about it each project I work on to see if it will all click some day Smile | :)
Also, the mix of accessibility levels (private, protected, internal) with keywords static, abstract, and override tend to leave me a little confused. How do I define this method/property/class....
It's not all a big mystery to me but some projects have me coding in circles when dealing with these topics.
With that said,
I have an application that reads an XML document and outputs text and image files. I’m also storing all of the information in a database. I have it working nicely.
The XML has a standard implementation with required fields and is used by multiple organizations to submit data to my app. All organizations should use (at least) the required nodes/elements that are outlined in the XML implementation guide.
So, I want to have a default data object type to be able to derive a specific organization’s data type for required elements. (If this object is going to be used, these are the fields that must be implemented).
If the org. just uses the default requirements, I can use the default object. If they use additional (optional) fields, I’ll have to create a new type inheriting the default type.
My first thought was to use and abstract class that had protected properties for my bare minimum requirements:
public abstract partial class AbstractDataObject
{
protected string DataObjectName;
protected DateTime? DataObjectDate;
etc...
}
Then, if the organization just uses the required elements of the node and no optional elements, I can use a “default” object.
internal partial class DefaultDataObject : AbstractDataObject
{
public new string DataObjectName { get; set; }
public new DateTime? DataObjectDate { get; set; }
etc...
}
But, if an organization uses optional fields of the required node, I can use a derived organization data object.
internal sealed partial class OranizationDataObject : AbstractDataObject
{
public new string DataObjectName { get; set; }
public new DateTime? DataObjectDate { get; set; }
etc...
//Optional fields used by this organization
public string DataObjectCode { get; set; }
etc...
}
Do I need the abstract class? It seems to me I can just have a DefaultDataObject (something like):
internal partial class DefaultDataObject
{
public virtual string DataObjectName { get; set; }
public virtual DateTime? DataObjectDate { get; set; }
etc...
}
And then:
internal sealed partial class OranizationDataObject : DefaultDataObject
{
public override string DataObjectName { get; set; }
public override DateTime? DataObjectDate { get; set; }
etc...
//Optional fields used by this organization
public string DataObjectCode { get; set; }
etc...
}
I’m just really trying to understand how to define these objects so I can reuse them per organization. Both ways seem to work, but I am hoping to understand how to define them properly.
Getting the XML into above objects:
public DefaultDataObject ExtractXmlData(XContainer root)
{
var myObject = (from t in root.
Elements("ElementA").Elements("ElementB")
select new DefaultDataObject()
{
DataObjectName = (String)t.Element("ChildElement1"),
DataObjectDate =
Program.TryParseDateTime((String)
t.Elements("ChildElement2")
.ElementAtOrDefault(0)
),
etc....
OR
public OranizationDataObject ExtractXmlData(XContainer root)
{
var myObject = (from t in root.
Elements("ElementA").Elements("ElementB")
select new OranizationDataObject()
{
DataObjectName = (String)t.Element("ChildElement1"),
DataObjectDate = Program.TryParseDateTime(
(String)t.Elements("ChildElement2")
.ElementAtOrDefault(0)),
DataObjectCode = (String)t.Element("ChildElement3"),
etc....
Again, thanks for reading. Don't forget to tip your wait staff....
Joe
First of all, your base class doesn't need to be abstract if it's a plain DTO class. If you don't have any functionality that needs to be implemented differently by derived classes, you can simply make it a plain base class which will hold common properties.
Next, there is no point in declaring properties in the base class (abstract in your case), if you are going to hide them (using the new keyword). You first code snippet of DefaultDataObject unnecessarily creates a bunch of new properties with the same name. Remove them completely - they are already defined in the base class.
[Edit] I didn't notice this initially, and #svick warned me, that your base class actually contained fields instead of properties, which makes me wonder why you needed to add the new keyword at all. I went over your code quickly and saw them as properties. In any case, you should never expose public fields - at least change them to auto-implemented properties by adding the { get; set; } block.
In other words, this would simply work:
// this doesn't need to be abstract.
// just put all the common stuff inside.
public class BaseDO
{
// as svick pointed out, these should also be properties.
// you should *never* expose public fields in your classes.
public string Name { get; set; }
public DateTime? Date { get; set; }
}
// don't use the new keyword to hide stuff.
// in most cases, you won't need that's behavior
public class DerivedDO : BaseDO
{
// no need to repeat those properties from above,
// only add **different ones**
public string Code { get; set; }
}
As a side note, but nevertheless important IMHO, you should simplify naming (and make it more clearer what your code does). There is no need to repeat "DataObject" in every property name, for example. But since your code is probably only a simplified version, it doesn't matter.
Lastly, have you heard of XmlSerializer? You don't need to traverse the XML elements manually. It is enough to call XmlSerializer to both serialize and deserialize your data.
Everything I need to know I learned from Sesame Street
Scrub your class design hard to make sure you've identified everything that is the same and different. Play computer, so to speak, with your classes and see how they do the same, different, or the same thing but in different ways.
What is the same, different, same but differently will likely change as you play computer.
Think in general terms of the two pillars of OO Classes. Polymorphism and Inheritance
As you do the above that is. Not so much in terms of C# implementation per se.
How things clump into same vs. different will help drive implementation
And it's all relative.
More of same default behavior? Perhaps a concrete base class instead of abstract.
More of same thing, but differently? Perhaps an abstract class instead of concrete base class.
A default way of doing x? Perhaps a virtual method.
Everyone does the same thing, but no two the same way? A delegate perhaps.
Implementation Suggestions
Make methods and fields protected as a default. Private does not get inherited. Designs change, stay flexible. If something just has to be private, fine.
virtual means you can change implementation in a sub class. It does not mean you must.
Folks seem to under-utilize delegates. They're super for polymorphic methods.
There is nothing wrong with public fields. What's the practical difference between a public field and a public auto-implemented property? Nothing. They both directly return (or set) the underlying value. So what's the point of even bothering with properties? If you want to publicly expose an underlying value differently than it's "natural" state. For example, returning a number in a specific format. And of course you can have different properties for the same field.
A Property can have a get without a set. Or vice versa. Also get and set can have different access levels. Often you'll see this as a public get and a protected (or private) set.
It depends on what the derived types will want to do. If they are going to use the default implementation and only expand on it somehow, then having the default class as the non-abstract base class is fine.
On the other hand, if they are most likely going to re-implement the functionality, you should have an abstract base class (or an interface) and a separate default class.
If you for some reason don't know which one is it, you can let the inheritors choose by having an abstract base class and leaving the default class unsealed.
Also, looking at your code, it seems you misunderstand what the various keywords do. Most of the time, you do not want to use new like this. What it does is to define another member with the same name, unrelated to the original one. Also, there's no reason to override something if you don't want to change it. So, if you expect that the derived classes won't have to reimplement the properties, you don't have to make them virtual at all.
An abstract class can already implement things that can be inherited
public abstract class DataObjectBase
{
public string DataObjectName { get; set; }
public DateTime? DataObjectDate { get; set; }
}
A concrete class can add new properties and methods
public class DerivedDataObject : DataObjectBase
{
public int NewProperty { get; set; }
}
The properties DataObjectName and DataObjectDate are already available in the new class, because they are automatically inherited from the base class.
If the abstract class defined an abstract member, however, you would have to implement it in the derived class.
Say the base class defines
public abstract void SomeMethod(string name);
The the derived class has to do this
public override void SomeMethod(string name)
{
...
}
If your base class does not have abstract members, it does not need to be abstract and can play the role of your default data object directly.
The keyword 'partial` is not needed here. It is only useful if you want to split one class into several pieces over several files.
The keyword new is wrong here. It is used to shadow an inherited member. This means that the inherited member will be hidden "behind" the new declaration. What you need, is to override. This does not hide a member, but provide an alternative implementation of the same member in the derived class.
Let's say you have two types of object, one that derives from the other but adds a single piece of extra functionality. The two ways I can think to deal with this extra functionality are adding an empty method on the base class that is always called (the derived class can then override this method) or explicit type checking to see if you have an instance of the derived class and then calling the extra method.
Both of these seem like hacks, is there a better way? If not is one preferred over the other? Both ways would work but neither seems particularly clean, one way you are polluting the base class with useless method stubs, the other way you are using explicit type checking which is usually considered a bad idea.
Here's an example to make it clear what I mean:
public class Weapon
{
// Should there be an empty StartCharging() here?
public virtual void Fire()
{
// Do something
}
}
public class ChargedWeapon : Weapon
{
public void StartCharging()
{
// Do something
}
public override void Fire()
{
// Do something
base.Fire();
}
}
public class Game
{
private Weapon weapon;
public void HandleUserInput()
{
if (MouseButton.WasPressed())
{
// Or should there be an if (weapon is ChargedWeapon) here
weapon.StartCharging();
}
else if (MouseButton.WasReleased())
{
weapon.Fire();
}
}
}
It's better to add the method to base class instead of doing a type check. What will happen if you do a typecheck and then decide to implement a new type of weapon which also needs charging? Will you add another test condition?
Edit: In your code, I see a start for an implementation of Strategy Pattern. I guess that your use case will benefit greatly from it and from State Pattern. If you need more details on these, leave a comment (as they are a little offtopic from the initial question's point of view)
Definitely don't do Type Checking here.
The big question is why you are dealing with a type Weapon and then calling StartCharging on it in your Game class? The implication in this code is that all Weapons implement StartCharging - if they do not, then you have already diverged from good OO practices.
Instead of this I would create an abstract method such as Initialise on Weapon. - In your Concrete Weapon classes implement this in different ways - e.g. for ChargedWeapon you would use:
public override void Initialise()
{
StartCharging();
}
for different weapons, the implementation would differ, e.g. For a HolsteredWeapon it might be:
public override void Initialise()
{
DrawWeapon();
}
In these example, only ChargedWeapon classes need to contain a StartCharging() method, and only HolsteredWeapon classes need to contain a DrawWeapon() method. However, every weapon needs an Initialise method.
Now the base type only contains methods which apply to ALL concrete implementations, so we are once again following good OO principles.
IMHO it is better to let the weapon(class) handle its own logic without exposing to much of its internal designs.
So simply add two methods like with the pattern startAction()/stopAction() in this case startFiring()/stopFiring() and the weapons decides if it needs to charge first/fire a single shot/fire burst/continuous fire...
Better way is to do:
public interface IChargable
{
void StartCharging();
}
public interface IWeapon
{
void Fire();
}
public class Weapon : IWeapon
{
public void Fire()
{ }
}
public class ChargedWeapon : Weapon, IChargable
{
public void StartCharging ()
{ }
}
private Weapon weapon;
public void HandleUserInput()
{
if (MouseButton.WasPressed() && weapon is IChargable)
{
((IChargable)weapon).StartCharging();
}
else if (MouseButton.WasReleased())
{
weapon.Fire();
}
}
Edit: Suppose you need to add a new weapons that is not chargeable too like "ExtraWeapon, SupperWeapon" , then you can see that using that empty method "StartCharging" for all the weapons that is not support it is useless and a bad design, furthermore you may have other methods or properties to set in that new types when MouseButton... so checking the type and only use its prepare methods/properties is a better choice.
I have just learned how to mask a base class member (using new) but am missing the point as to why I would want to do that. Does masking provide us with a certain level of protection as is the case in using encapsulation? Please advise.
You will very rarely use "new" to mask a base class member.
It's mainly used for the cases where the derived class had the member first, and then it was added to the base class --- the same name for a different purpose. The new is there to that you acknowledge that you know you are using it differently. When a base member is added in C++, it just silently merges the existing method into the inheritance chain. In C#, you will have to choose between new and override, to show you know what is happening.
It's not just used for masking. It actually breaks the inheritance chain, so if you call the base class method, the method in the derived class will not be called (just the one in the base class).
You're essentially creating a new method that has nothing to do with the base class method. Hence the "new" keyword.
Keeping that in mind the "new" keyword can be used if you want to define a method with the same signature as a base type method, but having a different return type.
The only valid safe examples that I've come across is being more specific with return types or providing a set accessor on a property. I'm not saying those are the only ones, but that's all I've found.
For example, suppose you have a very simple base that looks like this:
public abstract class Base
{
public string Name { get; protected set; }
public Base(string name)
{ Name = name; }
}
You could have a derived that looks more like this:
public class Derived : Base
{
public new string Name
{
get { return base.Name; }
set { base.Name = value; }
}
public Derived(string name) : base(name)
{ }
}
Assuming business rules allows this one specific Derived to have a changeable name, I believe this is acceptable. The problem with new is that it changes behavior depending on what type the instance is viewed as. For example, if I were to say:
Derived d = new Derived("Foo");
d.Name = "Bar";
Base b = d;
b.Name = "Baz"; // <-- No set available.
In this trivial example, we're fine. We are overriding the behavior with new, but not in a breaking way. Changing return types requires a bit more finesse. Namely, if you use new to change a return type on a derived type, you shouldn't allow that type to be set by the base. Check out this example:
public class Base
{
public Base(Base child)
{ Child = child; }
public Base Child { get; private set; }
}
public class Derived
{
public Derived(Derived child) : base(child)
{ }
public new Derived Child
{ get { return (Derived)base.Child; } }
}
If I could set Child on the Base class, I could have a casting problem in the Derived class. Another example:
Derived d = new Derived(someDerivedInstance);
Base b = d;
var c = b.Child; // c is of type Base
var e = d.Child; // e is of type Derived
I can't break any business rules by treating all of my Derived classes as Bases, it's just convenient to not type check and cast.
I have just learned how to mask a base class member (using new)
FYI this feature is usually called "hiding" rather than "masking". I think of "masking" as clearing bits in a bit array.
am missing the point as to why I would want to do that.
Normally you don't want to. For some reasons to use and not use this feature, see my article on the subject from 2008:
http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx
Does masking provide us with a certain level of protection as is the case in using encapsulation?
No, it does not.
What you are referring to is called Name Hiding. It is mostly a convenience feature. If you are inheriting from a class for which you do not control the source using new will let you change the behavior of a method even if it wasn't declared as virtual (or completely change the signature if it is virtual). The new keyword simply suppresses a compiler warning. You are basically informing the compiler that you are intentionally hiding the method from a parent class.
Delphi had the reintroduce keyword for the same reason.
What does this buy you other than a suppressed warning? Not a whole lot. You can't access the new method from a parent class. You can access it from an interface if your child class directly implements the interface (as apposed to inheriting it from its parent class). You can still call the parent class' member from the child. Any additional descendants of your class will inherit the new member rather than the one in the parent.
This is actually called member hiding. There are a couple of common scenarios where this can be appropriately used.
It allows you to work around versioning issues in which either the base or derived class author unwittingly creates a member name that collides with an existing identifier.
It can be used to simulate covariance on return types.
Regarding the first point...it is possible that an author of a base class could later add a member with the same name as an exisiting member in a derived class. The base class author may not have an knowledge of the derived classes and thus there is no expectation that she should avoid name collisions. C# supports the independent evolution of class hierarchies using the hiding mechanisms.
Regarding the second point...you may want a class to implement an interface that dictates a certain method signature and so you are locked into returning instances of a certain type only while at the same time you have subclassed that type and would really like for callers to see the concrete type instead. Consider this example.
public interface IFoo { }
public class ConcreteFoo { }
public abstract class Base
{
private IFoo m_Foo;
public Base(IFoo x) { m_Foo = x; }
public IFoo Foo { get { return m_Foo; } }
}
public class Derived
{
public Derived(ConcreteFoo x) : base(x) { }
public new ConcreteFoo Foo { get { return (ConcreteFoo)base.Foo; } }
}
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?
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.
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";
}
}
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; }
}
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();
}
//...
}
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.
#Aaronaught hit the nail on the head with the plugin-like architecture comment.
What I did the last time I encountered this, was to have a "Descriptor" type that was not terribly expensive to create, and keep the meta data in an instance field.
public class F16Descriptor : AircraftDescriptor
{
public override string Name { get { return "Lockheed Martin F-16 Fighting Falcon"; } }
public override Type AircraftType { get { return typeof(F16); } }
}
public class F16 : AircraftBase
{
...
}
An interesting way to solve this problem is to recognize that aircraft types are also an important concept in the design and create them as separate classes, whose instances act as types of aircrafts. This is known as the type object pattern (pdf), and it allows for very flexible designs.