Location to create objects in Command Base Program

I have enjoyed reading trough many teams Command Based programming and learn a lot of new way to get things done. One thing I did notice is when some teams create motors and other IO that they create only the address of the device in the RobotMap file and reference this in the subsystem that uses and owns the device when they create the instance of the object in the Subsytem.

The other way and is how Robotbuilder does this is creates the object in RobotMap and then creates a final private copy of this object in the Subsystem.

What is the benefit to either way of this?

I think robotmap being just a list of ports (for quick changes to say, retrofit your robot code to work on your testboard) and the subsystems having the objects (So you know what actuators and sensors are in subsystem’s domain when looking at the code) is the way to go.

We also keep everything port/channel related in RobotMap so that it is a one stop shop for the electrical team (or others) to see where things should be wired.

Do not do what RobotBuilder does and declare global objects in RobotMap. This is abysmal practice - it is a source of ongoing disappointment for me that an officially-supported FIRST resource would push teams in this direction. A very good rule-of-thumb is to never have any global (in Java, this means “public static”) program state unless you absolutely have to.

In fact, even the non-RobotBuilder-specific instructions from ScreenSteps promote similar bad practices; they suggest that teams declare subsystems themselves globally, and reference them directly from commands. Don’t do this; it is far better to declare subsystems privately and pass them to commands through a constructor or a setter.

In order to understand why these practices are terrible, it is important to understand some fundamental notions in object-oriented programming.

The power of object-oriented programming lies largely in encapsulation. The substance of encapsulation is restriction of your program’s information flow. OOP allows you to strictly control how parts of your code talk to one another. Doing this allows you to remove dependencies by loosely coupling code through well-defined interfaces; this is beneficial for flexibility (if a service is hidden behind an interface, internal details can later be changed without having to change any of the client code that uses the service) and for readability (by the same token, if client code doesn’t need to worry about how a service implements its functionality, neither, really, does the programmer while they are working with the client). If you have strict controls on how two parts of code can “talk” to one another, it is much easier to change them independently.

If one thinks of object-oriented design as imposing strict standards for the way information flows through a program, global program state provides a way for parts of the code to circumvent the standards entirely. When one relies heavily on global state, distant parts of a piece of software can talk to each other in uncontrolled and hard-to-track ways.

Global program state is utterly unencapsulated. Any part of your program can directly access a global variable at any time. Thus, any change to a service declared as a global variable is likely to require changes to all the code that uses it - as your program grows in size, this rapidly becomes unmanageable. Likewise, it rapidly becomes very hard/impossible for a programmer to keep track of every place a global variable is used from - this makes the behavior of the software increasingly difficult to predict as it becomes harder and harder to figure out what the program state ought to be at any given time.

Since a motor should only belong to a single subsystem, keeping these objects encapsulated within their subsystem makes more logical sense. Also, keeping motor controller objects in a globally accessible location is more likely to lead to bugs like trying to set it to different values at the same time if it is accidentally included in multiple subsystems.

Absolutely. The only advantage of declaring things global is that you never have to worry about finding them. The much greater disadvantage is that you never know where your code might have inadvertently found something and taken action you can’t track down later when the robot suddenly starts doing loop-the-loops as the two pieces of code battle each other.

This all makes sense and is what I sort of understood as I have been learning more about Java and Object Oriented code. This chatter surfaced my own question to why Robotbuilder did it there way and not the “more correct” way. The fact is I seen it happen when I was trying to get a team that used Robot Iterative as their base to try Command Based. The programmer wanted to control the motor directly in the command using the object in Robotmap vs creating an setter in the subsystem.

Not Knowing the most inner working of Robotbuilder it would seem that it wouldn’t be tough to fix the issue since its already creating code in both files relevant to each other.

Certainly you should put constants into RobotMap, defining where everything is plugged in. And definitely, the command code should interface with the subsystem, and not with the motor directly. Yes, students should learn object oriented design, if you have the time and they have the attention-span.

That said, I recommend creating your low-level component objects within the init method of RobotMap. We do a lot of twiddling of component parameters on these objects. Doing it in one file makes the configuration easier to read and easier to review. Finding misconfigurations later becomes easier, which is a valuable thing.

In my real-life job, I would put the whole configuration into one XML file and then inject the components into subsystems. This ensures encapsulation and still puts the critical configuration into one location. For FRC, I think configuring everything in RobotMap is a good compromise.

On 449, we use Jackson to do this from a YAML file. It is not prohibitively difficult to set up (you do not need to use an implementation that is nearly so involved as what we have settled on; much of the complexity in our config file is there in order to support handling of polymorphic types and pass-by-reference during deserialization), though I wouldn’t recommend immediately attempting it as a new team.

Really, though, I cannot stress this enough: do not declare your components globally. If you really want to initialize them in RobotMap, declare them locally and pass them explicitly to the code that will use them; this way, at least, you can track their access without having to read literally the entire program. Writing getters for each component would admittedly be a lot of overhead for little gain, so this is one of the few times I think it’d be justified to just declare them as public (not global!) fields.

It can be hard to see why this is so important if your team is caught in the cycle of re-writing the code from scratch every year (I know this from first-hand experience) - but one of the reasons teams get stuck in that cycle in the first place (usually without realizing it) is precisely because code that is written without considering these issues is terribly difficult to re-use.