Использование интерфейсов как инструмент рефакторинга

В прошлой статье я упоминал уже о том, что буду рассказывать об интерфейсах (C#), и как они упрощают нашу жизнь.
(ЗАМЕЧАНИЕ: использование интерфейсов в программе упрощает архитектуру приложения, делает код более ясным, но в тоже время их «добавление» можно отнести и к реинжинирингу)
(ЗАМЕЧАНИЕ 2: в статье рассмотрим темы: интерфейс, создание пользовательского интерфейса, работа со «встроенными» интерфейсами)

Что такое интерфейс?

Здесь попробую описать свойства классов. Кто знаком – прошу к следующей части

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

Для интерфейса не действуют правила классов, класс может наследоваться от множества интерфейсов.
Из свойств интерфейса определяют: (спасибо MSDN =)

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

  • Невозможно создать экземпляр интерфейса напрямую.

  • Интерфейсы могут содержать события, индексаторы, методы и свойства в качестве членов.

  • Интерфейсы не содержат реализации методов.

  • Как классы, так и структуры способны реализовывать несколько интерфейсов.

  • Интерфейс может быть унаследован от нескольких интерфейсов.


И приведу пример интерфейса:

  1.   interface IPoint
  2.   {
  3.     int x
  4.     {
  5.       get;
  6.       set;
  7.     }
  8.     int y
  9.     {
  10.       get;
  11.       set;
  12.     }
  13.     void Summary(object Value);
  14.   }
* This source code was highlighted with Source Code Highlighter.


В данном примере мы реализовали интерфейс IPoint (заметьте, названия интерфейсов принято задавать, начиная с буквы I). В данном интерфейсе мы описали 2 свойства (x,y) и метод Summary, который на вход принимает объект.
Это означает, что любой класс, наследующийся от IPoint должен будет реализовать эти свойства и метод.

Задача

Теперь поставим перед собой задачу: в некоторой программе необходим алгоритм сортировки. Который будет сортировать различные объекты.

Самый простой подход – это написать код следующего содержания:
(Договоримся, что для сортировки будем использовать быструю сортировку Хоара, как самую удобную и простую в реализации)
  1. private static void QSort(int[] obj, int Right, int Left)
  2.     {
  3.       int x = obj[Right / 2 + Left / 2];
  4.       int RightCopy = Right, LeftCopy = Left;
  5.  
  6.       while (Right > Left)
  7.       {
  8.         while (obj[Right]>(x) && Right>Left) Right--;
  9.         while (obj[Left]<(x) && Right>Left) Left++;
  10.         if (Right >= Left)
  11.         {
  12.           int temp = obj[Right];
  13.           obj[Right] = obj[Left];
  14.           obj[Left] = temp;
  15.           Right--;
  16.           Left++;
  17.         }
  18.         if (Right > LeftCopy) QSort(obj, Right, LeftCopy);
  19.         if (RightCopy > Left) QSort(obj, RightCopy, Left);
  20.       }
  21.     }
* This source code was highlighted with Source Code Highlighter.


Но этот код будет действенен только для массивов типа int. Чтобы добавить работу с пользовательским классом, необходимо будет определить(реализовать) операции сравнения и перегрузить метод QSort, заменив типы у x, temp и obj. Мне кажется это неудобным способом. Такой код можно (и нужно) подвергнуть рефакторингу.

Создание интерфейса

Предлагаю решить данную задачу, создав новый интерфейс ISort.
Данный интерфейс будет определять единственный метод, который должен будет содержать реализацию сравнения двух объектов одного класса и возвращать 1 – первый объект больше второго, 0 объекты равны, -1 меньше.

Приведу его код:

  1.   interface ISort
  2.   {
  3.      int CompareWith(object Value);
  4.   }
* This source code was highlighted with Source Code Highlighter.


Теперь необходимо написать метод, который будет отвечать за сортировку данных.
(В качестве входного массива можем записать ISort[] – в чем и есть «прелесть» интерфейсов. Причем в этом интерфейсе можно вызывать только члены, определенные в этом интерфейсе)

Для этого создадим дополнительный статический класс, в котором определим «сие чудо»:

  1. static class SortMethod
  2.   {
  3.     public static void Sort(ISort[] obj)
  4.     {//Метод, кторый вызывает работу быстрой сортировки Хоара.
  5.       QSort(obj, obj.Length - 1, 0);
  6.     }
  7.  
  8.     private static void QSort(ISort[] obj, int Right, int Left)
  9.     {//Имплементация быстрой сортивки
  10.       ISort x = obj[Right / 2 + Left / 2];
  11.       int RightCopy = Right, LeftCopy = Left;
  12.  
  13.       while (Right > Left)
  14.       {
  15.         while (obj[Right].CompareWith(x) > 0 && Right>Left) Right--;
  16.         while (obj[Left].CompareWith(x) < 0 && Right>Left) Left++;
  17.         if (Right >= Left)
  18.         {
  19.           ISort temp = obj[Right];
  20.           obj[Right] = obj[Left];
  21.           obj[Left] = temp;
  22.           Right--;
  23.           Left++;
  24.         }
  25.         if (Right > LeftCopy) QSort(obj, Right, LeftCopy);
  26.         if (RightCopy > Left) QSort(obj, RightCopy, Left);
  27.       }
  28.     }
  29.   }
* This source code was highlighted with Source Code Highlighter.


Итак. Мы получили метод для сравнения и интерфейс. Теперь все, что остается реализовать – добавить в классы наследование от интерфейса ISort и реализовать метод CompareWith.
Для наглядности приведу пример такого класса:

  1. class SomeClass : ISort
  2.   {
  3.     int SomeValue = 0;
  4.  
  5.     public int ClassValue
  6.     {
  7.       get { return SomeValue; }
  8.       set { SomeValue = value; }
  9.     }
  10.  
  11.     public SomeClass(int Value)
  12.     {
  13.       SomeValue = Value;
  14.     }
  15.  
  16.     public int CompareWith(object Value)
  17.     {
  18.       if (!(Value is SomeClass))
  19.         throw new ArgumentException("Сравниваемое значение не является типом SomeClass");
  20.  
  21.       if (SomeValue > (Value as SomeClass).SomeValue)
  22.         return 1;
  23.       else
  24.         if (SomeValue == (Value as SomeClass).SomeValue)
  25.           return 0;
  26.         else return -1;
  27.     }
  28.   }
* This source code was highlighted with Source Code Highlighter.


Прокомментирую данный код:
Каждый экземпляр данного класса имеет поле int (можно создавать любое кол-во полей прим.), имеет публичное свойство ClassValue и реализацию метода CompareWith. Причем если объект не является экземпляром данного класса он выдаст исключение (что удобно – это избавит нас от ошибок)

И сама реализация главной функции main
  1. static void Main(string[] args)
  2.     {
  3.       SomeClass[] SCArray = new SomeClass[100];
  4.  
  5.       Random rnd = new Random();
  6.       for (int index = 0; index < 100; index++)
  7.       {
  8.         SCArray[index] = new SomeClass(rnd.Next(100));
  9.       }
  10.  
  11.       foreach (var x in SCArray)
  12.         Console.Write(x.ClassValue + " ");
  13.       Console.WriteLine();
  14.       Console.WriteLine();
  15.  
  16.       SortMethod.Sort(SCArray);
  17.  
  18.       foreach (var x in SCArray)
  19.         Console.Write(x.ClassValue + " ");
  20.  
  21.       Console.ReadLine();
  22.     }
* This source code was highlighted with Source Code Highlighter.


Здесь мы создали 100 экземпляров нашего класса, проинициализировали (случайными числами) и отсортировали. Количество кода, которое могло бы быть по сравнению с первым методом, уменьшилось, и он стал проще. Ведь на самом деле, если нам необходимо реализовать сортировки для >5 классов мы получаем превосходство и в понятности кода и в его компактности.

Даешь еще проще!

Есть также способ, который позволит нам сделать работу еще более простой:
Использование «стандартного» интерфейса IComparable
Он уже имеет описание метода CompareTo, который реализован также, как наш CompareWith. Поэтому мы можем создать класс, отнаследовать его от IComparable и определить в нем метод CompareTo. А затем либо написать свой метод сортировки, либо воспользоваться коллекцией List, в которой вызвать метод Sort. (Либо изначально использовать коллекцию SortedList – сортирует данные «на лету»)

Приведу новый вариант класса:
  1. class SomeClass2 : IComparable
  2.   {
  3.     int SomeValue = 0;
  4.  
  5.     public int ClassValue
  6.     {
  7.       get { return SomeValue; }
  8.       set { SomeValue = value; }
  9.     }
  10.  
  11.     public SomeClass2(int Value)
  12.     {
  13.       SomeValue = Value;
  14.     }
  15.  
  16.     public int CompareTo(object Value)
  17.     {
  18.       if (!(Value is SomeClass2))
  19.         throw new ArgumentException("Сравниваемое значение не является типом SomeClass");
  20.  
  21.       if (SomeValue > (Value as SomeClass2).SomeValue)
  22.         return 1;
  23.       else
  24.         if (SomeValue == (Value as SomeClass2).SomeValue)
  25.           return 0;
  26.         else return -1;
  27.     }
  28.   }
* This source code was highlighted with Source Code Highlighter.


Здесь мы унаследовались от другого «стандартного» интерфейса IComparable и определили метод CompareTo

И пример работы с List:
  1. static void Main(string[] args)
  2.     {
  3.       List<SomeClass2> SCArray = new List<SomeClass2>();
  4.       
  5.       Random rnd = new Random();
  6.       for (int index = 0; index < 100; index++)
  7.       {
  8.         SCArray.Add(new SomeClass2(rnd.Next(100)));
  9.       }
  10.  
  11.       foreach (var x in SCArray)
  12.         Console.Write(x.ClassValue + " ");
  13.       Console.WriteLine();
  14.       Console.WriteLine();
  15.  
  16.       SCArray.Sort();
  17.  
  18.       foreach (var x in SCArray)
  19.         Console.Write(x.ClassValue + " ");
  20.  
  21.       Console.ReadLine();
  22.     }
* This source code was highlighted with Source Code Highlighter.


Итак, вот мы и посмотрели, как с помощью интерфейсов можно отрефакторить программу, с целью создания более понятного и компактного кода. Рассмотренный пример, конечно же простой, но выполняем и для больших проектов. А также познакомились с интерфейсами.
Предыдущая часть
Начало
2012-09-20