2009-06-12 4 views
1

У меня возникла проблема с разработкой части моей программы (не написав ее, на этот раз!). Это трудно объяснить, не написав роман, поэтому я постараюсь быть кратким.Правильный способ компоновки интерфейса C#

В принципе, у меня есть программа, которая считывает/записывает параметры с части оборудования. В настоящее время он делает это через Serial, но в конце концов, мне понравится это сделать через USB, используя .NET-обертку для чипа FTDI. http://www.ftdichip.com/Projects/CodeExamples/CSharp.htm

Я думаю, что моя проблема в том, что я знаю, что хочу несколько уровней абстракции , но я не могу понять, где рисовать линии. Во-первых, я не хочу, чтобы мои функции ReadParam(), WriteParam() и SendCommand() были в моем основном классе форм. Это просто кажется мощеным. Поэтому очевидно, что они должны быть в каком-то другом классе, который я создам. Назовем это Comm.

Первый вариант: я мог бы сделать интерфейс, допустим, IComm, и мои интерфейсы Serial и USB оба реализуют это. Проблема в том, что большой процент кода будет дублироваться в обоих вариантах, потому что у меня есть специальные ReadReplyData() и другие функции, которые выполняют предварительную обработку последовательных данных, прежде чем возвращать их в графический интерфейс.

Итак, следующая опция: Comm является промежуточным классом, который определяет интерфейс ICommDriver. Comm будет реализовывать частную функцию форматирования ReadReplyData(), а также общественность ReadParam(), WriteParam() и SendCommand(), а ICommDriver будет указывать только более простые функции Read и Write.

Все это кажется тривиальным, за исключением двух поворотов. Во-первых, я хочу, чтобы это было многозадачное, очевидно, поэтому GUI не зависает. Поэтому я думаю, что Comm будет использовать BackgroundWorker для выполнения всех операций чтения/записи. Кроме того, Serial-аромат нужно сообщить, какой порт COM открыть (из раскрывающегося меню GUI), в то время как USB-аромат не работает. Так я делаю эту часть интерфейса или нет?

Спасибо за помощь всем, я писал/удалял код в течение нескольких дней, пытаясь выяснить правильный способ сделать это!

Джонатона

ответ

7

Я знаю, что хочу несколько слоев абстракции, но я не могу знать где рисовать линии

Это где ваша проблема. Это принципиально ошибочный подход к развитию, и именно это приводит к этому параличу. Разработайте несколько бетонных вариантов реализации ароматов. Попросите их работать в вашем приложении с kludgy if type1 else type2 логикой. Затем вернитесь и отредактируйте их все, чтобы разделить общий договор, общую базу, что у вас есть. Это будет ослепляющим явным, что нужно идти куда.

С более подробно в комментариях:

Если общий код между реализациями, вы должны использовать абстрактный класс. По моему опыту, лучше всего держать открытые методы окончательными и вызвать защищенные абстрактные методы из открытых методов, например, так:

public interface IComm 
{ 
    void WriteParam(...); 
} 

public abstract class CommStandardBase : IComm 
{ 
    public void WriteParam(...) 
    { 
     DoWriteParam(...); 
    } 

    private void DoWriteParam(...) 
    { 
     CommonWrite1(...); 
     HandleWriteParam(...); 
     CommonWrite2(...); 
    } 

    protected abstract void HandleWriteParam(...); 

    private void CommonWrite1(...) 
    { 
     ... 
    } 

    private void CommonWrite2(...) 
    { 
     ... 
    } 
} 

сделать каждый класс самодостаточным. Он должен быть однострочным, однопоточным и может передаваться между рабочими и журналистами.

+0

У меня уже есть программное обеспечение, и я пытаюсь реорганизовать и очистить код. Как я уже упоминал, я ненавижу наличие функций 'ReadParam()' и 'WriteParam()' в моем классе MainForm. Эти два варианта в основном представляют собой оболочку класса .NET SerialPort и класс FTD2XX_NET FTDI. Таким образом, они почти идентичны, за исключением основных функций, которые они называют. –

+1

@ Jonathon, что не так с абстрактным классом, который реализует общую функциональность? –

+0

Я согласен с этой реализацией, когда у вас есть проблема с общим кодом. –

1

в отношении какого именно вида интерфейсов вам нужно, то есть в конечном счете, зависит от вас и кто знает, как это приложение работает на этом минимуме уровня. Я хотел бы ответить на часть об использовании ваших реализаций в пользовательском интерфейсе и комментарий о Comm с использованием BackgroundWorker. Я бы рекомендовал инвертировать это. BackgroundWorker - это действительно компонент уровня пользовательского интерфейса ... но Comm будет скорее «центральным» компонентом (например, бизнес-объектом в корпоративном приложении). Пользовательский интерфейс должен создать BackgroundWorker, который затем создает экземпляры Comm для выполнения требуемой работы и организует любые события из вашего Comm для обновления пользовательского интерфейса. Если вам требуется, чтобы ваш пользовательский интерфейс и BackgroundWorker взаимодействовали в течение длительного времени, я бы рекомендовал создать какой-то объект передачи данных, который может создать ваш пользовательский интерфейс, опустить очередь и использовать ручки с ручным управлением RiseetEvent или AutoResetEvent для связи между вашим пользовательским интерфейсом и BackgroundWorker. Это должно дать вам более свободно связанный продукт, позволяющий вам независимо создавать свой класс Comm любого пользовательского интерфейса, который может отображать его (возможно, вы можете иметь WindForms, WPF, командную строку и, возможно, даже клиенты PowerShell.)

1

Я несколько нахожусь в режиме VB.NET на momement так что здесь идет ...

 

Interface IComm 

    Function ReadParam() 
    Function WriteParam() 
    Function SendCommand() 

End Interface 



> 


MustInherit Class CommBase 

.... Load this up with the overideable 

End Class 


 

Тогда просто реализовать интерфейс и наследовать базу, если это необходимо. Я также согласен с Rex M. Не подталкивайте его слишком далеко для ослабления сцепления.

Смежные вопросы