2

I have real-world materials that are defined by various properties, some common, some not, that I would like to map to objects in C#. For example, Concrete has properties A, B, C, and Metal has properties A, B, D, E.

This looks like a textbook case for class inheritance:

class Material
{
    public double A { get; set; }
    public double B { get; set; }
}
class Concrete : Material
{
    public double C { get; set; }
}
class Metal : Material
{
    public double D { get; set; }
    public double E { get; set; }
}

This could work for the time being, however, with code maintainablility in mind, I can think of some caveats: the code performs calculations according to a given standard, but in the end game, it should be adaptable to other standards, which may require different properties to the object, or even change the object caracterization (derived classes). This would lead to something like this:

class Material
{
    public double A;
    public double B;
}
class Concrete : Material
{
    public double C;
}
class Metal : Material
{
    public double D;
    public double E;
}
class Concrete_US : Material
{
    ...
}
class Metal_US : Material
{
    ...
}
class Concrete_Europe : Concrete
{
    ...
}
class Steel_Europe : Metal
{
    ...
}
class Copper_Europe : Metal
{
    ...
}

Well, this is starting to get messy. But worse, a given material may very well be fully characterized across multiple standards, so an object might need to be an instance of multiple classes.

So I'm currently using another approach, which is to have a Properties dictionnary, mapping strings to values in a single Material class. That way, supporting a new material type and/or standard is just a matter of adding dictionnary entries. However, this feels error-prone, with risks of collisions and misspsellings (also string manipulation is inefficient, but there aren't many accesses to the dictionnary).

Is there a better way to do this?

  • If the only requirement is to store them, you can just use a List<object> for everything. Type specificity is only needed for retrieval, e.g. if you want to read properties or execute a method. The solution for your type system depends entirely on what sort of retrieval is needed-- which you have not specified. A correct answer will be elusive until you define those requirements. – John Wu Dec 12 '18 at 6:39
5

Don't try to make objects that simulate real world equivalents

Make objects that hold data that goes with their methods

In your materials example, you don't actually say what the data is used for.

If you have a function Compress(Material) for example, then you will need Material.CompressibilityFactor. You can define an Interface:

public interface ICompressible
{
     double CompressibilityFactor {get}
}

This doesn't mean things that don't inherit from ICompressible are physically incompressible. It means that the function in your code cant operate on them.

If you just need to store random properties and then display them, the the dictionary method if fine, as you have no functions that require specific properties

3

It does look like a textbook case for inheritance. Unfortunately most textbooks don’t mention inheritance is often not the best choice. As you noticed it can quickly create a giant and messy inheritance tree with lots of duplicated code. The Composition over Inheritance principle could apply here. Unfortunately you didn’t give much detail what your classes should do, but they could have behaviors like IBendBehavior, IMeltBehavior, ICompressBehavior, etc. Next you create various implementations for these interfaces, with their own distinct calculations. Finally you apply the specific implementations to your objects. You can even change implementations in runtime, allowing your clients to create entirely new materials with the behaviors they choose.

3

Try out an Entity Component System.

This technique takes the perspective that it does not know what a given Entity will look like at run time. You are already half-way there with you property bag system, which is not a bad way to handle the unknown. The problem is that these properties are not unknown, you just don't know which entities will have what set of them.

This is where the Component aspect shines. Each Component can define a full set of required properties, even stating a dependency on other components that are a part of the entity.

Finally the System provides a way to manage each component in a scale-able manner. It provides a manageable way to tune the system, change behaviour at run time, and fits nicely into simulation loops.

A number of other nice properties fall out of this system to including:

  • serialisation
  • memory management
  • extensability
  • reconfigurability
  • scriptable models

Obviously you may not need a full blown Entity Component System, if so take a look at Mixins.

  • My spidey sense is saying this abstraction helps thinking about fundamental structure which is not revealed by thinking about implementation per se - inheritance vs composition. As analogy I'm thinking about how ENIAC was programmed. The program is the physical wires, plugged into basic arithmetic machines (adding, or multiplying, etc.) - so these "very fundamental structures" are organizable in myriad ways, not pre-defined. i.e. manipulation underneath a inheritance/composition implementation as it were. – radarbob Dec 13 '18 at 8:32

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.