Можно ли избежать (многих) внутренних методов? (интерфейс, заводской шаблон) - PullRequest
3 голосов
/ 20 мая 2019

В настоящее время я работаю над ботом и имею около 8 отдельных классов для разных диалогов (все в одном пространстве имен!).Все они содержат разные задачи, и из-за этого у меня возникла небольшая проблема.Мне интересно, что было бы лучше: использование интерфейса, использование фабричного шаблона ... Однако все эти параметры вынуждают меня использовать внутренние методы.(И я получаю это с интерфейсом, потому что вы обещаете определенное поведение, но с фабричным шаблоном, которого я действительно не знаю - честно говоря.) Потому что нет никакого определения для конкретных методов - но это означает, что мне придется сделатьмногие из этих внутренних методов, и я пытаюсь избежать избыточности.

Сначала я просто создал экземпляр нового объекта, но вскоре понял, что это означает, что новый объект каждого вида / диалога создается каждый раз, когда был выполнен вызовсделано для бота - что не очень эффективно, верно?Я также пытался создать их экземпляр в конструкторе, но это вынуждает меня использовать интерфейс, и это дает мне ту же проблему, что я уже говорил ранее.Я также изучил частичные классы, но я не уверен, что использование 8 частичных классов действительно .. хорошо?

Сейчас я пробую фабричный шаблон с этим кодом: (Авторы этой ветки: Как предотвратить создание экземпляра объекта в c # )

public class DialogFactory
    {
        private NameDialog _nameDialog;
        private CertificateDialog _certificateDialog;
        private ProfileDialog _profileDialog;
        private ClassDialog _classDialog;
        private LocationDialog _locationDialog;
        private SkillDialog _skillDialog;
        private EducationDialog _educationDialog;
        private SpecializationDialog _specializationDialog;

        public DialogFactory CreateDialog(string dialog)
        {
            switch (dialog.ToLower())
            {
                case "name": return new NameDialog();
                case "certificate": return new CertificateDialog();
                case "profile": return new ProfileDialog();
                case "class": return new ClassDialog();
                case "location": return new LocationDialog();
                case "skill": return new SkillDialog();
                case "education": return new EducationDialog();
                case "specialization": return new SpecializationDialog();
                default: throw new Exception("That dialog does not exist.");
            }

            throw new Exception("That dialog does not exist.");
        }
    }

Чтобы дать некоторый контекст того, как выглядят диалоги, я также добавлю здесь диалог имен:

public class NameDialog : DialogFactory
    {
        ProfileService profileService = new ProfileService();

        public async Task AddNameResponse(ITurnContext turnContext, Profile profile, string value { … }
     }

Я пытаюсь получить доступ к Задаче AddNameResponse в основном методе следующим образом: await dialog.CreateDialog("name").AddNameResponse(turnContext, profile, value); Это не принято, однако я получаю следующее предупреждение: DialogFactory не содержит определения для AddNameResponse и не имеет доступного расширения.метод AddNameResponse, принимающий первый аргумент для DialogFactory.Исправление было бы внутренней задачей, но я пытаюсь избежать этого (причина этого указана ранее).

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

1 Ответ

2 голосов
/ 20 мая 2019

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

Если вы пытаетесь это сделать:

dialog.CreateDialog("name").AddNameResponse(turnContext, profile, value);

... и ошибка в том, что все, что возвращается из dialog.CreateDialog("name"), не имеет AddNameResponse метода, который показывает, что ваш код зависит от более специфического класса, чем тот, который возвращается из фабрики.

Завод не уменьшит сцепление по нескольким причинам:

  • Ваш код по-прежнему зависит от NameDialog. Вам нужен именно этот класс с его AddNameResponse методом.
  • Даже если фабрика вернула этот точный класс, теперь вы связаны с фабрикой и этого класса.

Имеет смысл, что вы хотите уменьшить связь, потому что если класс связан с NameDialog, он также связан с ProfileService. Невозможно протестировать класс, который зависит от NameDialog и не зависит от ProfileService.

Потенциальные решения будут включать изменение классов, которые зависят от NameDialog. Вот несколько мыслей:

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

Что каждый из них означает, что вы

  • Избегание связи в зависимости от абстракций
  • Предоставление контейнеру ввода зависимостей / IoC ответственности за создание объектов

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

...