4

さまざまなツールをピックアップして使用できるロボットを設計しているとします。使用したいツールを選択するためのPickupメソッドを持つRobotクラスを作成します。ツールごとに、Cutメソッドを持つKnifeクラスなどのクラスを作成します。RobotでPickupメソッドを呼び出した後、ロボットにカットするように指示します。では、OOPの概念については、ナイフではなくロボットに伝える必要がありますか?そして、CutメソッドはKnifeにあるので、どうすればそれを呼び出すことができますか?UseToolCurrentlyHeld()コマンドをKnifeに伝播するには、Robotに何らかの実装を行う必要があります。または、これを使用してナイフ(ロボットが保持している)を直接呼び出します。myrobot.ToolCurrentlyInHand.Cut()

親メソッドが、それらに含まれるクラスを処理するためにすべてを持たなければならないのは奇妙だと思います。ナイフのように、今では重複したメソッドがCut()あります。OOPの優れた方法は、ナイフを抽象化し、ナイフのような内部情報を気にせずにロボットを注文したように感じさせることであるため、ロボットはナイフでUseCutOnKnife()呼び出すだけで済みます。Cut()

もう1つの質問は、音楽を作成する場合、ノート情報を格納するためMusicの多くのクラスを含むクラスを作成することです。Measure1つのメジャーには、そのノートクラスに多くのNoteクラスが存在する可能性があります。このクラスには、メジャーのどこにこのノートが存在するか、またはそのノートが再生される時間などの情報が含まれます。ここで、小節のほぼ中央に配置された小節45に1つの音符を追加します。メジャーを作成するにはCreateMeasure(45)、音楽を呼​​び出してからメジャーを呼び出す必要がありCreateNote(0.5f)ますか?作成する方法はそのような親にありますか?そして今、そのノートをメジャーで0.25に変更したい場合、ノートを変更するメソッドを持つ責任があるのは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
}

今のところ、音楽クラスはすべての上にありますが、音楽を通じてすべてを発行する必要がありますか?そして、メソッドをチェーンして、最終的に最も内側のクラスを呼び出しますか?

4

7 に答える 7

5

ここで必要なのは、すべてのツールが実装する共通のインターフェースです。

例えば:

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

そして、次のようにPickupを呼び出すことができます。

 Robot robot = new Robot();
 robot.Pickup(new Knife());
 robot.UseTool();
于 2012-06-13T08:04:15.537 に答える
3

代わりに、別のアプローチを提案します。ロボットKnifeが選択できる他のすべてのオブジェクトはItem、メソッドを使用してジェネリッククラスから継承しUseます(インターフェイスにすることもできます)。

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
于 2012-06-13T08:02:06.957 に答える
1

基本的にはRobot、メソッドを使用してクラスを作成することをお勧めしますPickup(Tool tool)Tool具体的なツールクラスを継承するインターフェイスです。

PetarIvanovの答えを見てください。彼は私が何を意味するのかを詳細に説明します。

于 2012-06-13T08:03:39.577 に答える
1

ロボットの例として、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ツールがあることを知る必要があるだけですが、それは必ずしもそれらが何であるかを気にする必要はなく、それらがどのように動作するかは絶対に気にしません。標準のインターフェースを介してそれらを使用するだけです。ツールには追加のメソッドとデータを含めることができますが、この例では含まれていません。

于 2012-06-13T08:10:28.143 に答える
1

質問のロボットの部分について、みんながあなたにいい答えをくれました。

音楽の部分については、そうするかどうかはわかりません。特に、メジャー自体の中にメジャーの位置を保持することはしません。ノートとその位置についても同じことが言えます。私があまり好きではないもう一つのことは、あなたの位置が常に絶対値のように見えるという事実であり、これは既存の音楽を変更することになると役に立ちません。ここで何かが足りないかもしれませんが、CreateMeasure(45)を実行し、音楽にすでに90の小節がある場合はどうなりますか?以下のすべてのメジャーを更新する必要があります。ノートの位置については、ある種の「絶対的な」位置、つまりノートが次々と続くという事実だけでなく、いつノートを演奏するかを表すためにフロートを使用していると思います。私は私が思う dは、durationプロパティとPauseクラスを導入することを好みます。最後に、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
}
于 2012-06-13T14:08:07.670 に答える
0

Action<>は助けることができると思います、そしてここにそれについてのいくつかの有用な情報がありますアクションと機能それは私が理解するのを助けました、ActionそしてFuncそれで一般的にそれは素晴らしい投稿です。

于 2012-06-13T08:01:55.160 に答える
0

以下に示すように、焦点が何であるか、ロボットに何かを使用するように指示するか、ロボットに何かを実行するように指示するか、あるいはその両方に依存します。

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."
于 2012-06-13T08:10:06.383 に答える