C# 对父类隐藏实现细节

C# 对父类隐藏实现细节,c#,oop,C#,Oop,假设我正在设计一个机器人,它可以拾取各种工具并使用它们。 我将创建一个Robot类,它具有拾取方法来拾取我想要使用的工具。 对于每种工具,我都会为其创建一个类,比如说,刀子类,它具有Cut方法。 在机器人上调用拾取方法后,现在我想告诉我的机器人切割。所以对于OOP的概念,我必须告诉机器人,而不是刀子?而Cut方法是在刀子上的,那么我如何调用它呢?我必须在机器人上实现某种useToolCurrentlyHold(),以便将我的命令传播到刀子上。或者我直接使用以下命令调用刀子(我的机器人持有的刀子)

假设我正在设计一个机器人,它可以拾取各种工具并使用它们。 我将创建一个Robot类,它具有拾取方法来拾取我想要使用的工具。 对于每种工具,我都会为其创建一个类,比如说,刀子类,它具有Cut方法。 在机器人上调用拾取方法后,现在我想告诉我的机器人切割。所以对于OOP的概念,我必须告诉机器人,而不是刀子?而Cut方法是在刀子上的,那么我如何调用它呢?我必须在机器人上实现某种
useToolCurrentlyHold()
,以便将我的命令传播到刀子上。或者我直接使用以下命令调用刀子(我的机器人持有的刀子):
myrobot.toollcurrentlyinhand.Cut()

我觉得很奇怪,一个父方法必须拥有一切来处理它们所包含的类。现在我有了重复的方法,比如刀子有
Cut()
,而现在机器人必须有
UseCutOnKnife()
才能在刀子上调用
Cut()
,因为良好的OOP实践是将刀子抽象出来,让它感觉像是在订购机器人,而不必担心刀子之类的内部信息

另一个问题是,如果我创作音乐,我会创建
音乐
类,其中包含许多
度量
类来存储音符信息。在一个度量中,可以有多个
Note
类,其中的Note类将包含诸如此Note在度量中的位置或该Note播放的时间等信息。现在我想在尺寸45上加一个注释,放在尺寸的中间。要创建度量值,我必须在音乐上调用
CreateMeasure(45)
,然后在度量值上调用
CreateNote(0.5f)
?要创建的方法是在父对象上的吗?如果现在我想把音符改为0.25,那么负责改变音符的方法是
note
类本身还是
measure
类?或者我必须实现在最顶层的
音乐
类上更改音符的方法

这是我的课程概述:

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
}
课堂音乐
{
列表度量值=新列表();
//方法。。。
}
阶级尺度
{
内测计数器;
列表注释=新列表();
//方法。。。
}
课堂笔记
{
float positionInMeasure;//从0到1
}

因为现在音乐课是最重要的,我现在必须通过音乐发布所有内容?链接方法以最终调用最里面的类?

我认为
Action
可能会有所帮助,下面是一些关于它的有用信息,它帮助我理解
Action
Func
,所以一般来说,这是一篇很棒的文章。

我建议一种不同的方法。具有
和机器人可以拾取的所有其他对象,通过方法
使用
(也可以是接口)从通用类
继承:

现在,实现
项的每个类都将有自己的
使用
方法实现其特定操作

然后,
机器人
持有一个通用的
项目
,并在不知道它实际是什么的情况下对其调用
Use

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
类。
Tool
是从中继承具体工具类的接口


看一下Petar Ivanov的回答。他详细解释了我的意思。

这里您需要的是一个所有工具都可以实现的通用接口

例如:

现在刀可以实现该接口:

interface Item
{
    void Use();
}

class Knife : Item
{
    public void Use()
    {
        // cut action
    }
}
class Knife : ITool {
    public void Use() {
        //do the cutting logic
    }
}
现在,您可以通过机器人上的ITool界面使用该工具,但不知道它是什么工具:

class Robot {
    private ITool currentTool = null;

    public void Pickup(ITool tool)
    {
        currentTool = tool;
    }

    public void UseTool() {
        currentTool.Use();
    }
} 
你可以这样调用皮卡:

 Robot robot = new Robot();
 robot.Pickup(new Knife());
 robot.UseTool();

这取决于重点是什么,告诉机器人使用某些东西,或者告诉机器人做某些事情,或者两者兼而有之,如下所示:

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);
    }
}
用法:

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."

以机器人为例,在C#中,我将从以下内容开始:

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's holding the tool he's being told to drop
        tools.Remove(newTool);
    }

    public void UseTool(Tool toolToUse)
    {
        // you might check here if he's holding the tool,
        // or automatically add the tool if he's 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
    }
}
公共类机器人
{
私有IList工具=新列表();
公共无效拾取工具(工具新工具)
{
//你可以在这里检查他是否已经添加了工具
工具。添加(newTool);
}
公共作废工具(工具旧工具)
{
//你应该在这里检查一下他是否拿着别人叫他放下的工具
工具。移除(新工具);
}
公用工具(工具库)
{
//你可以在这里检查一下他是否拿着工具,
//或者如果他没有拿着工具,自动添加工具,等等。
toolToUse.Use();
}
}
公共接口工具
{
无效使用();
}
公共级刀具:工具
{
公共用途()
{
//剪一些
}
}
公共类锤子:工具
{
公共用途()
{
//敲打
}
}

所以
机器人只需要知道它有工具,但它不一定在乎告诉他们什么,也不在乎他们如何操作。它只是通过一个标准接口使用它们。这些工具可以包含其他方法和数据,但在本例中它们不包含这些方法和数据。

对于问题的机器人部分,每个人都给了你一个很好的答案

至于音乐部分,我不确定我是否会那样做。特别是,我不会将度量值的位置保留在度量值本身中,音符及其位置也是如此。另一件我不太喜欢的事情是,你的位置似乎总是绝对值,这对修改现有音乐没有帮助。也许我在这里遗漏了一些东西,但是当你执行CreateMease(45)并且你的音乐中已经有90个度量值时会发生什么呢?您必须更新以下所有度量值。对于音符位置,我想象你使用float来表示一种“绝对”位置,即何时弹奏音符,而不仅仅是它在另一个音符之后。我想我更喜欢引入一个持续时间属性
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."
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's holding the tool he's being told to drop
        tools.Remove(newTool);
    }

    public void UseTool(Tool toolToUse)
    {
        // you might check here if he's holding the tool,
        // or automatically add the tool if he's 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
    }
}
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
}