2011-04-29 11 views
6

Jestem programistą, który pracuje głównie z urządzeniami osadzonymi (zaprogramowanymi za pomocą C i złożenia). Moja znajomość C# i OOP jest bardzo ograniczona (chociaż w razie potrzeby mogę sobie z tym poradzić). Mam pięć urządzeń, które łączą się z komputerem przez USB. Komputer wykonuje pewne obliczenia i wysyła wynik do urządzenia. Te same obliczenia są wykonywane dla każdego urządzenia, ale obliczenia są wykonywane inaczej. Dla każdego urządzenia mam aplikację C# Windows Forms, która działa i przesyła dane do urządzenia. Obecnie staram się połączyć pięć różnych aplikacji w jedną, dzięki czemu możemy łatwo wprowadzać zmiany, łatwo dodawać nowe urządzenia i mieć standardowy interfejs użytkownika. Mój problem polega na tym, że nie wiem dokładnie, jak to zrobić, ponieważ nie wiem, które urządzenie będzie używane do czasu uruchomienia. Próbuję uniknąć wielu instrukcji if i chciałbym móc umieścić każde urządzenie w oddzielnym pliku. Oto trochę psudo-kodu tego, o czym mówię.Jak można wdrożyć wiele klas, które mają te same metody, które robią różne rzeczy?

class Device //This is what EVERY device can do 
{ 
... 
DoWork1(); 
DoWork2(); 
DoWork3(); 
... 
} 

class Device1 
{ 
... 
DoWork1(); //This is how the work is done for this device 
DoWork2(); 
DoWork3(); 
... 
} 

class Device2 
{ 
... 
DoWork1(); //This is how the work is done for this device (not the same way as Device1) 
DoWork2(); 
DoWork3(); 
} 


public partial class frmMain : Form 
{ 
private (some kind of object or something) CurrentDevice; 
public frmMain() 
{ 
... 
//Determine what device (could be one of five) is currently being used 
CurrentDevice = (which device is currently being used) 
//Could be CurrentDevice = new Device1(); 
} 
} 

private void Button1_Click() 
{ 
CurrentDevice.DoWork1(); //But this could be Device1.DoWork1() or Device2.DoWork1(), depending on what device is currently being used (which was determined in the frmMain constructor) 
} 

Nie jestem pewien, ale myślę, że mogę użyć interfejsu lub może dziedziczyć klasę Device1 dla klasy urządzeń i zastąpić metody ... Ale nie wiem, jak bym mają jeden ogólny sposób powiedzenia CurrentDevice.DoWork1(), ponieważ CurrentDevice może być Device1 lub Device2.

Wszelkie pomysły będą mile widziane. Używam Visual Studio 2008 z .NET 3.5 na Windows XP SP3 i Windows 7.

Mam nadzieję, że opisałem problem wystarczająco dobrze. Jeśli nie, lub nie wspomniałem o czymś, co powinienem, proszę dać mi znać. Jestem nowy w stackoverflow i C#.

Dziękuję

Michaela

+0

Chciałbym podziękować wszystkim za pomoc i sugestie. Postanowiłem pójść z rozwiązaniem dostarczonym przez Syzyfa, ponieważ pasuje on do mojej obecnej aplikacji. Wszyscy mieli na ten temat świetny wkład i dziękuję wam wszystkim. :) – Michael

Odpowiedz

4

byłem trochę zdezorientowany na początku, bo w tym przypadku komputera obsługuje obliczenia urządzenia otrzymują tylko wynik. Rozumiem, że potrzebujesz różnych implementacji czegoś na PC, a nie samych urządzeń.

Prawdziwa sztuczka nie polega na używaniu interfejsu lub dziedziczenia - już to wymyśliłeś. Sztuczka polega na uzyskaniu odpowiedniego typu implementacji, a do tej części używasz fabryki.

Ale musisz także zdecydować o dziedziczeniu w stosunku do interfejsu.

Używaj dziedziczenia tylko wtedy, gdy "coś" jest naprawdę częścią wspólnej, ale także znaczącej rodziny. Dziedziczenie powinno mieć bardzo silny element "jest".

OTOH może istnieć wiele obiektów, które mogą wykonywać obliczenia, ale możesz nie chcieć tworzyć rodziny. To tutaj jest przydatna kompozycja. Aby uzyskać to przez dziedziczenie, musisz mieć wspólną klasę podstawową. W tym miejscu można użyć kompozycji, aby umożliwić każdemu obiektowi korzystanie ze wspólnego interfejsu, aby umożliwić komputerowi wykonywanie obliczeń.

Proponuję takie podejście.

Powinieneś mieć odniesienie do wspólnego, ogólnego interfejsu, IDoCalculation lub czegoś takiego, który definiuje sygnaturę metody, która będzie wywoływana w taki sam sposób dla dowolnego urządzenia.

Następnie trzeba uzyskać implementację urządzenia dla tego interfejsu, gdzie każde urządzenie może mieć inną implementację. Utwórz klasę dla każdego typu urządzenia/implementacji.

Teraz wystarczy uzyskać klasę, której potrzebujesz, nie wiedząc, co to jest. Aby po raz kolejny zachować szczegóły ukryte i sprawić, by metoda była generyczna, można utworzyć sparametryzowaną Fabrykę. Ta fabryka akceptuje parametr opisujący urządzenie, którego komputer potrzebuje do obliczenia. Następnie interpretuje ten parametr i na tej podstawie tworzy określoną klasę, która implementuje IDoCalculation. To jest zwracane i gotowe.

zostawiam to do ciebie, aby dowiedzieć się, w jaki sposób powinny być zorganizowane w różnych zespołów tych obiektów ...

//Common interface 
public interface IDoCalculation 
{ 
    //Use whatever method signatures you need 
    int DoCalculation(); 
} 

public class DeviceImplementation1 : IDoCalculation 
{ 


    #region IDoCalculation Members 

    public int DoCalculation() 
    { 
     //Device 1 Specific code goes here 
    } 

    #endregion 
} 

public class DeviceImplementation2 : IDoCalculation 
{ 


    #region IDoCalculation Members 

    public int DoCalculation() 
    { 
     //Device 2 Specific code goes here 
    } 

    #endregion 
} 

// A simple factory that does not require a lot of OOP understanding. 
public class DeviceCalculationFactory 
{ 
    //Return a correct implementor based on the device type passed in 
    public IDoCalculation GetCalculationImplementationInstance(string devicetype) 
    { 
     switch (devicetype) 
     { 
      case "Device1": 
       return new DeviceImplementation1(); 

      case "Device2": 
       return new DeviceImplementation2(); 
      default: 
       //TODO ??? 
       return null; 

     } 

    } 

} 

// A simple client that calls the methods and then send the results 
public class DeviceManager 
{ 
    //To do the calculation, get an implementor for the correct device type from the factory - Presumably the PC knows the device of interest, example "Device1" 
    public void DoTheCalculationThing(string deviceType) 
    { 


     DeviceCalculationFactory factory = new DeviceCalculationFactory(); 
     IDoCalculation calculation = factory.GetCalculationImplementationInstance(deviceType); 
     int result = calculation.DoCalculation(); 
     // now send the result to the device 

    } 

} 
1

Możesz być zainteresowany patrząc na niektóre wzorców projektowych dla tego produktu. http://www.oodesign.com/

W szczególności streszczenie Fabryka i metoda szablonów. Myślę, że jedno z nich może być tym, czego szukasz. http://www.oodesign.com/abstract-factory-pattern.html
http://www.oodesign.com/template-method-pattern.html

Jak rozumiem, chcesz mieć możliwość klasy bazowej, a następnie dziedziczyć funkcje klasy bazowej i określić ich podklasy. Jeden z tych wzorów prawdopodobnie zadziałałby dla twojego scenariusza.

+1

+1 za sugerowanie metody szablonu. To jest droga. @Anthony, możesz mieć domyślną implementację w swojej abstrakcyjnej klasie bazowej, a klasy pochodne mogą przesłonić tylko określone metody. –

8

W twoim przypadku zasadniczo definiujesz hierarchię dziedziczenia, która może składać się z abstrakcyjnej klasy bazowej i dwóch wyprowadzonych typów lub interfejsu z dwoma implementatorami. Na przykład

public abstract class BaseDevice 
{ 
    public abstract void DoWork1(); 
} 

public class Device1 : BaseDevice 
{ 
    public override void DoWork1() 
    { 
     // provide implementation here 
    } 
} 

// also create Device2 : BaseDevice and implement 

czy można wykorzystać definicję interfejsu

public interface IDevice 
{ 
    void DoWork1(); 
} 

public class Device1 : IDevice 
{ 
    public void DoWork1() 
    { 
     // provide implementation 
    } 
} 

Które metodologia wybrać należy do Ciebie. Być może preferowałbyś abstrakcyjną klasę podstawową, gdybyś na przykład chciał zdefiniować pewne zachowanie lub właściwości za pomocą implementacji wspólnych dla całej hierarchii. Za pomocą klasy abstrakcyjnej możesz zapewnić implementacje. Interfejs jest pustą umową, nie można zapewnić żadnych typowych zachowań, a jedynie definicję tego, jakie zachowania lub właściwości mogą występować.

Tak czy inaczej, można odwołać się do instancji bardziej pochodnego typu za pomocą abstrakcji lub podstawy interfejsu. W ten sposób nie obchodzi cię, jaki jest typ implementacji, tylko co może zrobić (to metody lub właściwości).

Przykład:

BaseDevice device1 = new Device1(); 
BaseDevice device2 = new Device2(); 
// maybe you have a list? 
List<BaseDevice> devices = new List<BaseDevice> { device1, device2 }; 

foreach (BaseDevice device in devices) 
{ 
     device.DoWork1(); // notice you don't care about the actual type, just the behavior 
} 
0

odpowiedź Anthony Pegram jest doskonała, ale może chcesz wziąć go o krok dalej. Można sobie wyobrazić, że chociaż wydaje się, że wszystkie twoje urządzenia wykonują te same zadania, może się okazać, że niektóre z nich nie wykonują zadań, a jeszcze inne wykonują jeszcze więcej.

W takich przypadkach może być skłonny do zmiany interfejsu, aby dodać kolejną DoWork5 lub DoWork6 metodę i po prostu podnieść NotImplemented wyjątki dotyczące typów, które nie mają konkretnego zachowania.

Jest to kłopotliwe z wielu powodów. Proponuję (jeśli znajdziesz się na tym stanowisku), aby rzucić okiem na robienie wyraźnych ról. Robisz to, tworząc interfejsy, które reprezentują określoną rolę (lub zbiór zachowań --- wiąże się z zasadą segregacji interfejsu).

Więc można mieć IMediaPlayer z Play, Pause, Rewind i innymi IMediaRecorder z metodą Record. W ten sposób implementujesz odpowiednie role na swoich konkretnych zajęciach.

HTH

Powiązane problemy