2012-06-13 2 views
4

Предположим, что я разрабатываю робот, который может захватывать различные инструменты и использовать их. Я бы создал класс роботов, у которого есть метод Pickup, чтобы выбрать инструменты, которые я хотел использовать. Для каждого инструмента я бы создал для него класс, скажем, класс Ножа, который имеет метод Cut. После вызова метода «Пикап» на роботе теперь я хочу сказать, чтобы мой робот разрезал. Итак, для концепции ООП я должен сказать роботу, а не ножу? И метод Cut на ноже, так как я могу это вызывать? Я должен реализовать какой-то UseToolCurrentlyHeld() на Robot для распространения моих команд на Knife. Или я просто прямо называю нож (который мой робот удерживает) напрямую, используя это: myrobot.ToolCurrentlyInHand.Cut()Скрытие функциональных возможностей из родительских классов

Мне кажется странным, что родительский метод должен иметь ВСЁ, чтобы заботиться о классах, которые они содержат. Теперь у меня есть повторяющиеся методы, например, у Ножа есть Cut(), и теперь у Робота должно быть UseCutOnKnife() только для вызова Cut() на Нож, потому что хорошая практика ООП заключается в том, чтобы абстрагировать Нож и заставить его чувствовать, что приказывает Роботу, не беспокоясь о внутренней информации, такой как Нож.

Другой вопрос, если я сочиняю музыку, я бы создал класс Music, который содержит много классов Measure для хранения информации о заметке. В одном измерении внутри него может быть много классов Note, класс Note будет иметь такую ​​информацию, как, где эта мера проживает эта заметка, или как долго играет эта заметка. Теперь я хочу добавить одну ноту на меру 45, помещенную примерно на середину меры. Чтобы создать Мера, я должен позвонить CreateMeasure(45) на Music, а затем позвонить 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 
} 

Как теперь класс музыка находится на вершине все теперь должны выдавать все через музыку? И связать метод, чтобы наконец назвать самый сокровенный класс?

+0

+1 за пахнущие что-то неладное, и артикулировать его – razlebe

ответ

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

И вы можете вызвать такой прием:

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

+1 Это именно то, что я имею в виду :) – davioooh

+0

Так что в данном случае это, как использовать() на Ноже в паре с UseTool() на роботе? Как насчет моего музыкального дела? В сценарии «Робот» Нож создается при непосредственном вызове экземпляра робота. Теперь для создания новой заметки об измерении, как я могу получить прямой вызов в экземпляре меры, зная, что музыка инкапсулирует его? – 5argon

+0

относительно первого вопроса: вам не нужно иметь UseTool - вы можете просто вызвать функцию Use on currentTool. Не уверен, что я понимаю ваш второй вопрос –

0

Я думаю Action<> может помочь и вот некоторая полезная информация об этом Action and Func Это помогло мне понять Action и Func так вообще отличный пост.

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 
1

В основном я предлагаю вам создать Robot класс с a Pickup(Tool tool) метод. Tool - это интерфейс, из которого наследуются классы конкретных инструментов.

Взгляните на ответ Петра Иванова. Он подробно объясняет, что я имею в виду.

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." 
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 нужно знать только, что у него есть инструменты, но не обязательно, что они сказали, и определенно не важно, как они работают. Он просто использует их через стандартный интерфейс. Инструменты могут содержать дополнительные методы и данные, они просто не для этого примера.

1

Все дали вам хороший ответ на вопрос Робота.

Для музыкальной части я не уверен, что я сделаю это именно так. В частности, я бы не сохранил позицию Мериды внутри самой Мериды, и то же самое касается примечания и ее положения. Еще одна вещь, которая мне не очень нравится, - это то, что ваши позиции кажутся всегда абсолютными значениями, и это не помогает, когда дело доходит до изменения существующей музыки. Может быть, мне что-то не хватает, но что произойдет, когда вы выполните CreateMeasure (45), и в вашей музыке уже есть 90 мер? Вам нужно будет обновить все следующие Меры. Для позиции «Нота» я предполагаю, что вы используете float, чтобы иметь возможность представлять своего рода «абсолютную» позицию, то есть когда играть ноту, а не только то, что она приходит после другого. Я думаю, что я предпочел бы ввести свойство продолжительности и класс 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 
} 
Смежные вопросы