Class within class within class... etc. function calls

I was wondering about what the consensus is on calling functions from a parent class that are in a child class, e.g.:

ClassA.ClassB.ClassC.ClassD.ClassE.CallFunction();

Where ClassE is in ClassD, which is in ClassC, which is in ClassB, which is in ClassA.

Is it more advisable to do this every time instead:

// in a method in ClassA
CallFunction() { ClassB.CallFunction(); }
// in ClassB
void CallFunction() { ClassC.CallFunction(); }
// in ClassC
void CallFunction() { ClassD.CallFunction(); }
// in ClassD
void CallFunction() { ClassE.CallFunction(); }
// in ClassE
void CallFunction() { /*Do thing I actually want to do*/ }

I know they effectively do the same thing, and it may not be necessary to do this every single time. If each of these child classes were private or protected, then obviously ClassA.ClassB.ClassC.etc wouldn’t work, since ClassA doesn’t have access to ClassC and so on. It would also make calling CallFunction() already readily available to those in-between classes, preventing the need to rewrite code that does the same thing. Just curious what you all typically do?

If you’re nesting classes, especially that many, then you might want to rethink what you’re doing.

1 Like

It doesn’t really matter as long as it makes sense to you. You can do any atrocities you want in your own code. :smiley:

Do you have a specific problem you’re trying to solve? Maybe we can help better if you have some code to show.

It’s not really a problem of functionality, rather it’s aesthetics. For e.g.:

class Director
{ 
    List<Team> teams;
    void GoHome()
    { 
        for (int i = 0; i < teams.Count; i++)
        { teams[i].GoHome(); }
    }

    void MessyWayOfCalling_GoHome()
    {
        for (int i = 0; i < teams.Count; i++)
        {  
            Team team = teams[i];
            for (int j = 0; j < team.combatants.Count; j++)
            {    
                Combatant combatant = team.combatants[j];
                combatant.GoHome(); 
            } 
        } 
     }
     void CleanWayOfCalling_GoHome()
     {
         GoHome();
     }
}
class Team 
{ 
    List<Combatant> combatants; 
    public void GoHome()
    {
        for (int i = 0; i < combatants.Count; i++)
        { combatants[i].GoHome(); }
}
class Combatant 
{ 
    Vector3 position;
    Vector3 homePosition; 
    public void GoHome()
    { position = homePosition; }
} 

This is quick representation of how I use nested classes. Both the messy and clean functions do the same thing, but what if you have to call something that’s nested deeper, like each Combatant has a Weapon, each Weapon has a list of Bullets, etc. etc. If Director.AffectAllBullets() happens, I suppose I could just hold a POOL_ of weapons or bullets, which each Combatant would pull from. I feel that’s more of a bandaid, which makes the Director class more bloated.

There’s a better approach to programming something like this that I could benefit from learning. I know how to make it functional, but I don’t always write things clean.

Ultimately you can do it however you like, but if you’re going to end up maintaining your code in any way (which is extremely likely), go with the cleaner approach. Specifically, follow Single Responsibility… ensure your methods (even classes) are only responsible for one task.

As you add to your code, or refactor it later, only having one reason to change any particular method or class makes things a lot easier. There’s less stuff to pull apart and rework.

If you’re worried about your Director (and other classes) getting bloated, break that functionality out into smaller pieces. I’ll take an ECS-like approach here…

class Director
{
  public List<Team> Teams { get; private set; } = new List<Team>();
}

class GoHomeSystem
{
  public void GoHome(Director director)
  {
    // the stuff in messy, accessing teams via director.
  }
}

// some place you call this from..
Director d;
GoHomeSystem s;
s.GoHome(d.Teams);

You can also get more refined if you want and aggregate each individual sub behaviour…

class CombatantGoHomeSystem
{
	public void GoHome(Combatant c)
	{
		c.Position = c.HomePosition;
	}
}

class TeamGoHomeSystem
{
	private CombatantGoHomeSystem _system;
	public TeamGoHomeSystem(CombatantGoHomeSystem system)
	{
		_system = system ?? throw new ArgumentNullException();
	}
	
	public void GoHome(Team t)
	{
		foreach (var combatant in t.Combatants)
			_system.GoHome(combatant);
	}
}

class DirectorGoHomeSystem
{
	private TeamGoHomeSystem _system;
	public DirectorGoHomeSystem(TeamGoHomeSystem system)
	{
		_system = system ?? throw new ArgumentNullException();
	}

	public void GoHome(Director d)
	{
		foreach (var team in d.Teams)
			_system.GoHome(team);
	}
}

// Some caller...
CombatantGoHomeSystem combatantGoHomeSystem = new CombatantGoHomeSystem();
TeamGoHomeSystem teamGoHomeSystem = new TeamGoHomeSystem(combatantGoHomeSystem);
DirectorGoHomeSystem directorGoHomeSystem = new DirectorGoHomeSystem(teamGoHomeSystem);

Director d;

directorGoHomeSystem.GoHome(d);

Anyway, this is all just suggestion I whipped up on the fly. The bottom line is, it’s good to break your code up :slight_smile:

2 Likes

Hmm, I like it. Instead of having any of the in-between nested classes worry about or acknowledge that functionality, create a class that is specifically designed to handle the calling of that function (and allowing it to handle the messy work).

Its also called the DRY principle - Don’t Repeat Yourself. The moment you catch yourself copying and pasting code around, you might want to create a method for it, and call that instead of copying the code around. It leaves the problems in one place. Needless to say, if the method contained a bug, you’d be able to fix it once, in one place, and it solves the same problem everywhere the code was called from.

1 Like