Когда расширять интерфейс, ограничивать его или использовать для этого реализацию - PullRequest
1 голос
/ 22 октября 2011

У меня часто возникают проблемы с определением, когда расширять интерфейс или уменьшать его или использовать для этого какую-либо реализацию. Рассмотрим матрицу; он может быть квадратным или нет, поэтому я бы сказал:

interface IMatrix<T>
{
  void InsertColumn(int position);
  void InsertRow(int position);
  void DeleteColumn(int position);
  void DeleteRow(int position);
  T ValueAt(int row, int col); //gets the value
  void ValueAt(int row, int col, T value); //sets the value
}

Итак, вышеуказанная матрица может быть квадратной, но это не обязательно. Моей первой идеей было использование класса для создания идеи квадратной матрицы как

class SquareMatrix<T> : IMatrix<T>
{
   private IMatrix<T> matrix;
   public SquareMatrix(IMatrix<T> matrix) {this.matrix = matrix;}
   public void InsertColumn(int position)
   {
     // Must ensure that a row and column are both added to keep it square
     this.matrix.InsertRow(position);
     this.matrix.InsertColumn(position);
   }
   // rest of methods similarly perform a row and column operation to keep square...
 }
}

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

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

interface ISquareMatrix<T>
{
  void InsertRow(int position);// adds a row and column, name selected for inheritance purposes
  void DeleteRow(int position);// deletes a row and column, ...
  void ValueAt(int row, int col, T value);
  T ValueAt(int row, int col);
}

interface IMatrix<T> : ISquareMatrix<T>
{
  void InsertColumn(int position);// adds only a column and now InsertRow should not also insert a column
  void DeleteColumn(int position);// deletes only a column
}

Это выглядит нормально, но несколько нарушает мой ход, хотя и относится к наследованию, так как матрица SquareMatrix IS-A, а не наоборот. Кроме того, поскольку я выбрал свои имена таким образом, чтобы наследство казалось нормальным, я стал осторожнее с этим подходом.

abstract class MatrixBase<T> : IMatrix<T>
{
  //Base class simply provides a matrix that we can use
  IList<IList<T>> matrix = new List<IList<T>>();
  public MatrixBase(int rows, int columns)
  {
    if(rows < 1) {/*throw ...*/}
    if(columns < 1) {/*throw...*/}
    // add rows and columns
  }

  public abstract void InsertColumn(int position);
  // more abstract methods to implement interface
}
class Matrix<T> : MatrixBase<T>
{
  //override all methods so that insert column only inserts a column
  //and insert row only inserts a row
}
class SquareMatrix<T> : MatrixBase<T>//seems fishy since matrix base is an IMatrix
{
  //override all methods so that insert row inserts a row and column
  //and insert column calls insert row
}

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

Во всяком случае, это мои мысли; Как бы вы смоделировали отношения между квадратной матрицей и матрицей, которой не должно быть квадрата? :) Спасибо!

1 Ответ

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

Как правило, интерфейс должен включать только методы, которые все реализации могут разумно выполнять. В этом случае я думаю, что методы AddRow и AddColumn не должны быть частью интерфейса, а должны быть методами Matrix.

.

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

...