Как реорганизовать толстый интерфейс? - PullRequest
4 голосов
/ 24 июня 2010

Допустим, у меня есть следующий набор интерфейсов ....

 public interface IStart
        {
            void M1();
            bool IsWorking { get; }
        }

    public interface IStartStop : IStart
        {
            void M2();
                   event EventHandler DoM1;
            event EventHandler DoM2;
        }
    public interface IPreferencesReader : IStartStop, IDisposable
        {
            string CustomColumnDefinition       {get;}
                bool UsePricelevelDetection  {get;}
            void InitializePreferences();
        }

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

public class PreferencesReaderBase : IPreferencesReader
    {
        public void M1()
        {
            throw new NotImplementedException();
        }

        public bool IsWorking
        {
            get { throw new NotImplementedException(); }
        }

        public void M2()
        {
            throw new NotImplementedException();
        }

        public event EventHandler DoM1;
        public event EventHandler DoM2;

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        public string CustomColumnDefinition
        {
            get { throw new NotImplementedException(); }
        }

        public bool UsePricelevelDetection
        {
            get { throw new NotImplementedException(); }
        }

        public void InitializePreferences()
        {
            DoSomeInitialization();
        }
    }

Могу ли я применить какой-либо шаблон для этого сценария, чтобы выполнить его рефакторинг?

РЕДАКТИРОВАТЬ: я не могу обойтись без этой иерархии, как в не может удалить любой из интерфейсов.

Спасибо за ваш интерес.

Ответы [ 3 ]

2 голосов
/ 25 июня 2010

Я думаю, вам нужно разобраться, почему вы не можете разбить иерархию интерфейса.Вы говорите, что иерархия необходима, но некоторые классы не должны будут реализовывать все методы.Это означает, что иерархия не обязательна!

Если IPreferencesReader не нуждается в реализации метода M1, то это действительно не IStart.Контракт, который он заключает, является недействительным.

Помните, что классы могут наследовать несколько интерфейсов.Предполагая, что вы разбиваете иерархию наследования, тогда, если PreferencesReaderBase делает вещи IStop, но не вещи IStart, вы можете объявить это как:

public class PreferencesReaderBase : IPreferencesReader, IStop

Если по какой-либо причине вы не можете разделить этот интерфейсВы могли бы рассмотреть разделение классов реализации.

class Start
{
  M1() 
  {

  }
}

class Stop
{
  M2()
  { 

  }
}

public class PreferencesReaderBase : IPreferencesReader
{
  private Start start = new Start();
  private Stop stop = new Stop()
  public void M1()
  {
      this.start.M1();
  }


  public void M2()
  {
      this.stop.M2();
  }
}

Это, по крайней мере, сохранит основные функциональные классы аккуратными.Каждый класс выполняет одно действие, поэтому Принцип единой ответственности сохраняется.

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

2 голосов
/ 24 июня 2010

Вам не обязательно предоставлять рабочие реализации. В вашем примере это выглядит так, как будто ваш IPreferencesReader не нуждается в IStartStop, поэтому вы можете просто удалить его включение?

Если вы хотите скрыть метод интерфейса от реализующего типа (т. Е. Вы не увидите его в объектах PreferencesReaderBase), вы можете реализовать простоту интерфейса:

void IStart.Start() { }

После этого вы сможете вызывать эти методы интерфейса только путем приведения ваших ссылок PreferencesReaderBase к ссылкам IStart.

1 голос
/ 25 июня 2010

Используйте шаблон «Адаптер», а не one , который идентифицирует Википедия, поэтому, возможно, моя терминология нестандартна - но, как в Java MouseAdapter :

Абстрактный класс адаптера для получения событий мыши.Методы в этом классе пусты.Этот класс существует как удобство для создания объектов слушателя.

...

Расширьте этот класс, чтобы создать слушателя MouseEvent и переопределить методы для интересующих событий.(Если вы реализуете интерфейс MouseListener, вы должны определить все методы в нем. Этот абстрактный класс определяет нулевые методы для них всех, поэтому вам нужно только определять методы для событий, которые вас интересуют.)

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

Полный толстый интерфейс все еще существует;он все еще реализуется (через наследование) вашим классом - но код вашего класса будет включать только то, что важно.

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