对父类隐藏实现详细信息

本文关键字:详细信息 实现 隐藏 父类 | 更新日期: 2023-09-27 18:36:56

假设我正在设计一个机器人,它可以拾取各种工具并使用它们。我将创建一个具有拾取方法的机器人类来选择我想使用的工具。对于每个工具,我将为其创建一个类,例如具有Cut方法的Knife类。在机器人上调用拾取方法后,现在我想告诉我的机器人切割。所以对于OOP概念我必须告诉机器人,而不是刀?切割方法在刀上,所以我如何调用它?我必须在机器人上实现某种UseToolCurrentlyHeld(),以将我的命令传播到刀。或者我直接用这个直接调用刀(我的机器人握住的):myrobot.ToolCurrentlyInHand.Cut()

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

另一个问题,如果我创作音乐,我会创建一个包含许多Measure类来存储音符信息的类Music。在一个度量值中,其中可以有许多Note类,哪个 Note 类将包含诸如此注释驻留在度量值中的什么位置或该注释播放多长时间之类的信息。现在我想在措施的中间位置添加一条关于措施 45 的注释。要创建度量,我必须在音乐上调用CreateMeasure(45),然后在测量上调用CreateNote(0.5f)?创建的方法就是这样在父级上?如果现在我想将该注释更改为度量值上的 0.25,那么负责更改 Note 的方法的人是 Note 类本身还是Measure类?或者我必须实现更改最顶层Music类笔记的方法?

这是我的课程概述:

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
}

就像现在音乐课一样,我现在必须通过音乐发布所有内容?并链接方法以最终调用最里面的类?

对父类隐藏实现详细信息

这里需要的是所有工具实现的通用接口。

例如:

interface ITool {
    void Use();
}

现在刀可以实现该接口:

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();

我建议采用不同的方法。具有机器人可以从具有方法Use(也可以是接口)的泛型类Item继承的Knife和所有其他对象

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

现在,每个实现Item的类都将有自己的 Use 方法实现,以实现其特定操作。

然后,Robot持有一个通用Item,并在不知道它实际上是什么的情况下调用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 r = new Robot();
r.PickUpItem(new Knife());
r.UseItem(); // uses knife
r.PickUpItem(new Hammer());
r.UseItem(); // uses hammer

对于机器人示例,在 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
    }
}

因此,Robot只需要知道它有工具,但它不一定关心它们是什么,也绝对不在乎它们是如何操作的。 它只是通过标准接口使用它们。 这些工具可以包含其他方法和数据,只是此示例不包含。

基本上,我建议您使用 Pickup(Tool tool) 方法创建 Robot 类。 Tool 是一个从中继承具体工具类的接口。

看看佩塔尔·伊万诺夫的答案。他详细解释了我的意思。

每个人都给了你一个很好的答案。

对于音乐部分,我不确定我会这样做。特别是,我不会将度量值的位置保留在度量值本身中,注释及其位置也是如此。我不太喜欢的另一件事是,您的位置似乎总是绝对值,这在修改现有音乐时无济于事。也许我在这里遗漏了一些东西,但是当你执行 CreateMeasure(45) 并且你的音乐中已经有 90 个小节时会发生什么?您必须更新以下所有措施。对于音符位置,我想你使用浮点是为了能够代表一种"绝对"位置,即何时演奏音符,而不仅仅是它在另一个音符之后出现的事实。我认为我更愿意引入持续时间属性和暂停类。最后,我不会将方法从 Note 传播到 Music,但我会将属性保留为公共,正如您所说,我将链接方法以最终调用最里面的类。最后,我的类看起来类似于这些:

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
}
我认为

Action<>可以提供帮助,这里有一些关于它的有用信息 动作和功能 它帮助我理解ActionFunc所以总的来说,这是一个很棒的帖子。

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

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