Правильный способ выложить интерфейс C # - PullRequest
1 голос
/ 12 июня 2009

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

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

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

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

Джонатон

Ответы [ 3 ]

7 голосов
/ 12 июня 2009

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

Вот в чем ваша проблема. Это принципиально некорректный подход к развитию, и именно это приводит к этому параличу. Сначала разработайте несколько конкретных вариантов вкусов. Заставьте их работать в вашем приложении с логикой 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(...)
    {
        ...
    }
}

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

1 голос
/ 12 июня 2009

Сейчас я немного в режиме VB.NET, так что вот так ...


Interface IComm

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

End Interface



>


MustInherit Class CommBase

.... Load this up with the overideable

End Class



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

1 голос
/ 12 июня 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...