0

With C#, I am defining the physical structure and behavior of a robot.

For the physical structure: I wrote an abstract "Unit" class. Other classes such as "Factory" or "Arm" are derived from Unit. So there is a hierarchy/tree of Unit objects that defined the physical makeup of the robot. For example, the constructor of Factory would create instances of Arm among other things as children. And those children reference the Factory instance as their parent. Unit objects only have methods to help define initial physical configuration.

The problem and my question: The hierarchy can be wide. It could also be deep down to something like "OuterBearingRace" for a Bearing under Rotor under Actuator under Arm under Factory. There could potentially be hundreds of classes derived from Unit. Someone on another forum mentioned that this could be problematic. So why would it be problematic and how would you do it differently?

Unfinished example code:

public class Factory : Unit{
public Factory(string unitName,Unit parentUnit,Transform parentTransform,Vector3 position,Quaternion rotation) : base(unitName,parentUnit,parentTransform,position,rotation){
    AddUnit(new Platform("Platform",this,null,new Vector3(0,0,0),Quaternion.identity)); //Platform derived from Unit
    float armSpacing = 457.2f - 152.4f;
    float armHeight = U("Platform").P("mainMountHeight").v;
    AddUnit(new Arm("Arm1",this,null,new Vector3(armSpacing,armHeight,0),Quaternion.identity,304.8f,50f)); //Arm derived from Unit
    AddUnit(new EndMill("EndMill1",this,null,new Vector3(armSpacing+600f,armHeight+304.8f/2f,0),Quaternion.identity));
    U("Arm1").AddAction(new IKLock("LockOnEndmill",U("Arm1"),null,U("EndMill1").transform));
}

}

For behavior, a Unit can have a tree of actions such as "DrillHole" or "GrabBarStock" that are derived from an abstract "Action" class. It's the same scheme as the physical structure except methods are allowed to define dynamic behavior of the robot during run-time update calls.

Julian
  • 77
  • 4
  • Under what conditions might you treat an Arm the same way you would a Bearing Race? – whatsisname Sep 16 '20 at 21:42
  • Things that are common between an arm and bearing race: Both go into a solution explorer to display name and admin stuff, transform data such as position and orientation, visualization commands for a voxel mesh, etc. A Unit also has to reference other Units without knowing what kind of unit it is referencing. – Julian Sep 16 '20 at 21:49
  • Are you familiar with: (1) [the SOLID principles](https://en.wikipedia.org/wiki/SOLID), and (2) one or more code bases where the SOLID principles are applied along with step-by-step explanations of how they are actually applied? This will save us a lot of time in trying to explain some basic things to you. – rwong Sep 16 '20 at 22:17
  • These hundreds of "things" (rigid bodies; composite bodies; mechanical components) can implement interfaces. Hundreds of classes all implementing a certain interface (think about `IList` or `ICollection` in C#) is not a problem at all. A giant tree of interface hierarchies is not a problem at all. – rwong Sep 16 '20 at 22:18
  • Just to give an example ... suppose there is a single component that is actually a soft body (having some kind of elasticity or flexibility that is not precisely controlled). The existence of this component would open a loophole in the scheme of things. Meanwhile, if the `Unit` (or `object` in C#) does not bake in the idea of everything being rigid, there is no loophole. – rwong Sep 16 '20 at 22:23
  • *// Both go into a solution explorer to display name and admin stuff, transform data such as position and orientation, visualization commands for a voxel mesh, etc. //* This is where Interface Segregation Principle (ISP) would be applied. ISP is one of the five SOLID principles. – rwong Sep 16 '20 at 22:25
  • Diamonds between C# interfaces is a non-issue. If A, B, C, D are interfaces, if B and C extend A, and D extend B and C, there is no issue at all, except if some ambiguity arises that require the concrete implementation class to disambiguate using explicit interface member implementation. For example you can have an `IKettlePot` which is both an `IKettle` and an `IPot`. – rwong Sep 16 '20 at 22:30
  • 1
    So, it sounds like I do not want to use inheritance in this case. Instead, I will implement a "Unit" interface for things like Arm and BearingRace. But my main objective is to reuse code for something like physics behaviors. So to do that, I guess I can pass a Physics object to Arm and BearingRace on creation so they can reuse physics code for example (instead of inheriting the physics code from a Unit class). Thanks for the help everyone. – Julian Sep 16 '20 at 23:35
  • 1
    https://www.youtube.com/watch?v=aKLntZcp27M – Theraot Sep 16 '20 at 23:51
  • 1
    The problem with having a 100-class wide hierarchy, or a very deep hierarchy, besides a maintainability nightmare, is that it's unlikely that the supertypes will have enough shared behavior as to be useful, and that you'll be making a lot of wrong assumptions that will prevent you from changing the code later. What you want is to compose your objects out of several core components, that can be combined in different ways - you limit the number of types, but you can still combine them into different kinds of objects. (It then becomes about figuring out what these core components should be.) – Filip Milovanović Sep 17 '20 at 01:28
  • In other words, don't go for a distinct type for every kind of thing imaginable, but identify these core behaviors, and then represent different things by different composite objects. – Filip Milovanović Sep 17 '20 at 01:30

0 Answers0