Правильный путь для событий формы, чтобы добраться до приложения - PullRequest
1 голос
/ 25 октября 2011

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

Основная форма моего приложения имеет меню, содержащее функции отладки, и я ловлю события в коде формы. В настоящее время методы запрашивают конкретный объект в приложении, если он не нулевой, а затем связываются с ним. Я пытаюсь провести рефакторинг, чтобы я мог удалить ссылку на этот объект везде и использовать вместо него интерфейс (интерфейс используется многими другими объектами, которые не имеют отношения к функциям отладки.)

В качестве упрощенного примера представьте, что у меня есть этот логический код:

public class Logic
{
    public SpecificState SpecificState { get; private set; }
    public IGenericState GenericState { get; private set; }
}

И этот код формы:

private void DebugMethod_Click(object sender, EventArgs e)
{
    if (myLogic.SpecificState != null)
    {
        myLogic.SpecificState.MessWithStuff();
    }
}

Итак, я пытаюсь избавиться от ссылки SpecificState. Он был удален из любого другого места в приложении, но я не могу придумать, как переписать функции отладки. Должны ли они перенести свою реализацию в класс Logic? Если так, то что тогда? Было бы полной тратой помещать множество MessWithStuff методов в IGenericState, так как все другие классы имели бы пустые реализации.

редактировать

В течение жизни приложения многие IGenericState экземпляров приходят и уходят. Это DFA / шаблон стратегии. Но только одна реализация имеет функции отладки.

В сторону: Есть ли еще термин для «отладки» в этом контексте, относящийся к функциям только для тестирования? «Отладка» обычно относится только к процессу исправления ошибок, поэтому поиск такого материала затруднен.

Ответы [ 3 ]

1 голос
/ 25 октября 2011

Создайте отдельный интерфейс для хранения функций отладки, таких как:

public interface IDebugState
{
    void ToggleDebugMode(bool enabled); // Or whatever your debug can do
}

Затем у вас есть два варианта, вы можете либо ввести IDebugState так же, как вы вводите IGenericState, как в:

public class Logic
{
    public IGenericState GenericState { get; private set; }
    public IDebugState DebugState { get; private set; }
}

Или, если вы ищете более быстрое решение, вы можете просто выполнить тестирование интерфейса с помощью чувствительных к отладке методов:

private void DebugMethod_Click(object sender, EventArgs e)
{
    var debugState = myLogic.GenericState as IDebugState;
    if (debugState != null)
        debugState.ToggleDebugMode(true);
}

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

Внутренне, конечно, у вас все еще есть SpecificState, реализующий и IGenericState, и IDebugState, так что всегда есть только один экземпляр - но это зависит от вашего контейнера IoC, ни один из ваших зависимых классы должны знать об этом.

1 голос
/ 25 октября 2011

Я бы настоятельно рекомендовал прочитать руководство по внедрению зависимостей Ninject (обязательно прочитайте весь учебник). Я знаю, что это может показаться странной рекомендацией, учитывая ваш вопрос; тем не менее, я думаю, что в конечном итоге это сэкономит вам много времени и сохранит ваш код чище.

Ваш отладочный код зависит от SpecificState; поэтому я ожидаю, что ваши пункты меню отладки будут запрашивать у DI-контейнера свои зависимости, или у провайдера, который может вернуть зависимость, или null. Если вы уже работаете над рефакторингом для включения DI, то предоставление элементов меню отладки с соответствующими внутренними битами вашего приложения в качестве зависимостей (через контейнер DI), кажется, является подходящим способом для достижения этого без нарушения основополагающих принципов проектирования. Так, например:

public sealed class DebugMenuItem : ToolStripMenuItem
{
    private SpecificStateProvider _prov;

    public DebugMenuItem(SpecificStateProvider prov) : base("Debug Item")
    {
       _prov = prov;
    }
    // other stuff here

    protected override void OnClick(EventArgs e)
    {
        base.OnClick(e);


        SpecificState state = _prov.GetState();
        if(state != null)
           state.MessWithStuff();
    }
}

Предполагается, что экземпляр SpecificState не всегда доступен, и к нему необходимо обратиться через провайдера, который может вернуть ноль. Кстати, у этой техники есть дополнительное преимущество - меньше обработчиков событий в вашей форме.

Кроме того, я бы порекомендовал не нарушать принципы проектирования ради отладки, и чтобы ваши методы отладки "перепутались с вещами" взаимодействовали с вашими внутренними классами так же, как любой другой фрагмент кода должен - через его интерфейс " контракт». Вы избавите себя от головной боли =)

0 голосов
/ 25 октября 2011

Я был бы склонен взглянуть на внедрение зависимостей и декораторы для относительно больших приложений, как предложил FMM, но для небольших приложений вы могли бы сделать относительно простое расширение для своего существующего кода.

Я предполагаю, чтовы как-то толкаете экземпляр Logic в части вашего приложения - либо через static классы или поля, либо передавая в конструктор.

Затем я бы расширил Logic с помощью этого интерфейса:

public interface ILogicDebugger
{
    IDisposable PublishDebugger<T>(T debugger);
    T GetFirstOrDefaultDebugger<T>();
    IEnumerable<T> GetAllDebuggers<T>();
    void CallDebuggers<T>(Action<T> call);
}

Тогда глубоко внутри вашего кода какой-то класс, который вы хотите отлаживать, будет вызывать этот код:

var subscription =
    logic.PublishDebugger(new MessWithStuffHere(/* with params */));

Теперь в вашем коде верхнего уровня вы можете вызвать что-то вроде этого:

var debugger = logic.GetFirstOrDefaultDebugger<MessWithStuffHere>();
if (debugger != null)
{
    debugger.Execute();
}

Более короткий способ вызова методов в вашем классе отладки - использовать CallDebuggers следующим образом:

logic.CallDebuggers<MessWithStuffHere>(x => x.Execute());

Назад, глубоко в вашем коде, когда ваш класс, который вывы собираетесь выйти из области отладки, вы должны вызвать этот код, чтобы удалить его отладчик:

subscription.Dispose();

Это работает для вас?

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