Hide functionality from parent classes

Suppose I am developing a robot that can grab a variety of tools and use them. I would create a Robot class that has a Pickup method to select the tools I wanted to use. For each tool, I would create a class for it, say, a class of Knife, which has a Cut method. After calling the Pickup method on Robot, now I want to say that my robot is cutting. So, for the concept of OOP, should I tell the robot, not the knife? And the Cut method on the knife, so how can I call this? I need to implement some UseToolCurrentlyHeld() on Robot to distribute my commands on Knife. Or I just directly call the knife (which my robot holds) directly using this: myrobot.ToolCurrentlyInHand.Cut()

It seems strange to me that the parent method should have EVERYTHING to take care of the classes that they contain. Now I have repeating methods, for example, the Cut() Knife, and now the Robot should have UseCutOnKnife() only to call Cut() on the Knife, because OOP's good practice is to abstract the Knife and make it feel, what Robot orders without worrying about inside information such as a knife.

Another question, if I compose music, I would create the Music class, which contains many Measure classes to store information about the note. In one dimension, there can be many Note classes inside it; the Note class will have information such as to what extent this note is located, or how long this note plays. Now I want to add one note to measure 45, placed approximately in the middle of the measure. To create a measure, do I have to call CreateMeasure(45) in the Music section and then call CreateNote(0.5f) in the Measurement field? Is the creation method on the parent element? And if now I want to change this note by 0.25 per measure, then I am responsible for changing the method. Note: is it a Note class or a Measure class? Or should I implement a method for changing notes in the top-most Music class?

This is an overview of my classes:

 class Music { List<Measure> measures = new List<Measure>(); //methods... } class Measure { int measureNumber; List<Note> notes = new List<Note>(); //methods... } class Note { float positionInMeasure; //from 0 to 1 } 

How is the Music class now on top of everything I now need to pass everything through Music? And bind a method to finally name the innermost class?

+4
source share
7 answers

Here you need a common interface that all tools implement.

eg:.

 interface ITool { void Use(); } 

Now the knife can implement this interface:

 class Knife : ITool { public void Use() { //do the cutting logic } } 

Now you can use this tool through the ITool interface on the robot, not knowing which tool it runs:

 class Robot { private ITool currentTool = null; public void Pickup(ITool tool) { currentTool = tool; } public void UseTool() { currentTool.Use(); } } 

And you can call Pickup as follows:

  Robot robot = new Robot(); robot.Pickup(new Knife()); robot.UseTool(); 
+5
source

Instead, I propose a different approach. Have Knife and all other objects that the robot can choose to inherit from the general class Item using the Use method (it can also be an interface):

 interface Item { void Use(); } class Knife : Item { public void Use() { // cut action } } 

Now each class that implements Item will have its own implementation of the Use method for its specific action.

Then Robot contains a generic Item and calls Use on it, not knowing what it really is:

 class Robot { public Item CurrentItem { get; private set; } public void PickUpItem(Item i) { CurrentItem = i; } public void UseItem() { CurrentItem.Use(); // will call Use generically on whatever item you're holding } } 

...

 Robot r = new Robot(); r.PickUpItem(new Knife()); r.UseItem(); // uses knife r.PickUpItem(new Hammer()); r.UseItem(); // uses hammer 
+3
source

Basically, I suggest you create a Robot class using the Pickup(Tool tool) method. Tool is an interface from which classes of specific tools are inherited.

Take a look at the answer of Peter Ivanov. He explains in detail what I mean.

+1
source

For the robot example, in C #, I would start with something like this:

 public class Robot { private IList<Tool> tools = new List<Tool>(); public void PickUpTool(Tool newTool) { // you might check here if he already has the tool being added tools.Add(newTool); } public void DropTool(Tool oldTool) { // you should check here if he holding the tool he being told to drop tools.Remove(newTool); } public void UseTool(Tool toolToUse) { // you might check here if he holding the tool, // or automatically add the tool if he not holding it, etc. toolToUse.Use(); } } public interface Tool { void Use(); } public class Knife : Tool { public void Use() { // do some cutting } } public class Hammer : Tool { public void Use() { // do some hammering } } 

Thus, Robot only needs to know that he has the tools, but does not necessarily care about what they said, and it is definitely not important how they work. It just uses them through a standard interface. Tools may contain additional methods and data; they simply are not suitable for this example.

+1
source

Everyone has given you a good answer for the Robot part of the question.

For the musical part, I’m not sure that I will do so. In particular, I would not have kept Merida’s position within Merida itself, and the same goes for the note and its position. Another thing that I don't really like is that your positions always seem to be absolute values, and that doesn't help when it comes to modifying existing music. Maybe something is missing for me, but what happens when you run CreateMeasure (45) and there are already 90 measures in your music? You will need to update all of the following Measures. For the “Note” position, I assume that you are using a float to be able to represent a kind of “absolute” position, that is, when to play a note, and not just that it comes after another. I think I would prefer to introduce a duration property and a Pause class. Finally, I will not distribute methods from Note to Music, but I would leave the properties public, and as you say, I would bind methods to finally name the innermost class. In the end, my classes will look like the following:

 public class Music { public List<Measure> measures = new List<Measure>(); public Measure AddMeasure() { Measure newM = new Measure(); measures.Add(newM); return newM; } public Measure CreateMeasure(int pos) { Measure newM = new Measure(); measures.Insert(pos, newM); return newM; } public Measure CreateMeasureAfter(Measure aMeasure) { Measure newM = new Measure(); int idx = measures.IndexOf(aMeasure); measures.Insert(idx + 1, newM); return newM; } public Measure CreateMeasureBefore(Measure aMeasure) { Measure newM = new Measure(); int idx = measures.IndexOf(aMeasure); measures.Insert(idx, newM); return newM; } //methods... } public class Measure { public List<ANote> notes = new List<ANote>(); public void AddANote(ANote aNote) { notes.Add(aNote); } public void AddANote(int pos, ANote aNote) { notes.Insert(pos, aNote); } public void AddANoteAfter(ANote aNote, ANote newNote) { int idx = notes.IndexOf(aNote); notes.Insert(idx + 1, newNote); } public void AddANoteBefore(ANote aNote, ANote newNote) { int idx = notes.IndexOf(aNote); notes.Insert(idx, newNote); } //methods... } public abstract class ANote { public int duration; // something like 4 for a quarter note/pause and so on // your stuff } public class Note : aNote { float frequency; //or whatever else define a note // your stuff } public class Pause: aNote { // your stuff } 
+1
source

I think Action<> can help, and here is some useful information about it. Action and Func. It helped me understand Action and Func so this is a great post.

0
source

It depends on what the trick is, telling the robot to use something, or telling the robot about something, or maybe both, as shown below:

 public abstract class Task { public abstract void Perform(Robot theRobot); } public class Cut : Task { public string What { get; private set; } public Cut(string what) { What = what; } public override void Perform(Robot theRobot) { var knife = theRobot.ToolBeingHeld as Knife; if (knife == null) throw new InvalidOperationException("Must be holding a Knife."); knife.Use(theRobot); Console.WriteLine("to cut {0}.", What); } } public class Stab : Task { public override void Perform(Robot theRobot) { var knife = theRobot.ToolBeingHeld as Knife; if (knife == null) throw new InvalidOperationException("Must be holding a Knife."); knife.Use(theRobot); Console.WriteLine("to stab."); } } public class Bore : Task { public override void Perform(Robot theRobot) { var drill = theRobot.ToolBeingHeld as Drill; if (drill == null) throw new InvalidOperationException("Must be holding a Drill."); drill.Use(theRobot); Console.WriteLine("to bore a hole."); } } public abstract class Tool { public abstract void Use(Robot theRobot); public abstract void PickUp(Robot theRobot); public abstract void PutDown(Robot theRobot); } public class Knife : Tool { public Knife(string kind) { Kind = kind; } public string Kind { get; private set; } public override void Use(Robot theRobot) { Console.Write("{0} used a {1} knife ", theRobot.Name, Kind); } public override void PickUp(Robot theRobot) { Console.WriteLine("{0} wielded a {1} knife.", theRobot.Name, Kind); } public override void PutDown(Robot theRobot) { Console.WriteLine("{0} put down a {1} knife.", theRobot.Name, Kind); } } public class Drill : Tool { public override void Use(Robot theRobot) { Console.Write("{0} used a drill ", theRobot.Name); } public override void PickUp(Robot theRobot) { Console.WriteLine("{0} picked up a drill.", theRobot.Name); } public override void PutDown(Robot theRobot) { Console.WriteLine("{0} put down a drill.", theRobot.Name); } } public class Robot { public Robot(string name) { Name = name; } public string Name { get; private set; } public Tool ToolBeingHeld { get; private set; } public void PickUp(Tool tool) { if (ToolBeingHeld != null) ToolBeingHeld.PutDown(this); ToolBeingHeld = tool; ToolBeingHeld.PickUp(this); } public void PutDown() { if (ToolBeingHeld != null) ToolBeingHeld.PutDown(this); ToolBeingHeld = null; } public void Perform(Task task) { task.Perform(this); } } 

Using:

 var robot = new Robot("Fred the Robot"); robot.PickUp(new Knife("butcher")); // output is "Fred the Robot wielded a butcher knife." robot.Perform(new Cut("a leg")); // output is "Fred the Robot used a butcher knife to cut a leg." robot.Perform(new Stab()); // output is "Fred the Robot used a butcher knife to stab." try { robot.Perform(new Bore()); } // InvalidOperationException: Must be holding a drill. catch(InvalidOperationException) {} robot.PutDown(); // output is "Fred the Robot put down a butcher knife." 
0
source

All Articles