MPavlak
MPavlak

Reputation: 2221

Extension Methods which return interface type

So I was writing a simple generic matrix class and ran into a problem for which I do not like my solution so I figured I'd ask for help with a better one.

Consider the interface described here:

public interface IMatrix<T>
{
    void DeleteColumn(int position);
    void DeleteRow(int position);
    // Returns a NEW IMatrix<T>
    IMatrix<T> FromRows(IList<IList<T>> rows);      // would like to remove
    // Returns a NEW IMatrix<T>
    IMatrix<T> FromColumns(IList<IList<T>> columns);// would like to remove
    IList<IList<T>> GetColumns();
    IList<IList<T>> GetRows();
    void InsertColumn(int position);
    void InsertRow(int position);
    void SetValueAt(int row, int column, T value);
}

with extensions

public static class MatrixExtensions
{
    /// <summary>
    /// Performs a standard matrix addition
    /// </summary>
    public static IMatrix<T> Add<T>(this IMatrix<T> matrix, IMatrix<T> other, IScalarOperators<T> operators)
    {
        JoinCells<T> joiner = new JoinCells<T>();
        return joiner.Join(matrix, other, null, operators.OperatorAdd);
    }

    /// <summary>
    /// Adds a row to the end of the matrix
    /// </summary>
    public static void AddRow<T>(this IMatrix<T> matrix);

    /// <summary>
    /// Adds a number of rows to the end of the matrix
    /// </summary>
    public static void AddRows<T>(this IMatrix<T> matrix, int rows);

    /// <summary>
    /// Adds a column to the end of the matrix
    /// </summary>
    public static void AddColumn<T>(this IMatrix<T> matrix);

    /// <summary>
    /// Adds a number of columns to the end of the matrix
    /// </summary>
    public static void AddColumns<T>(this IMatrix<T> matrix, int columns);

    /// <summary>
    /// Gets the column at the specified position
    /// </summary>
    public static IList<T> ColumnAt<T>(this IMatrix<T> matrix, int position);

    /// <summary>
    /// Gets the number of columns in the matrix
    /// </summary>
    public static int ColumnCount<T>(this IMatrix<T> matrix);

    /// <summary>
    /// Sets the number of columns in the matrix
    /// </summary>
    public static void ColumnCount<T>(this IMatrix<T> matrix, int columns);

    /// <summary>
    /// Deletes the last column from the matrix
    /// </summary>
    public static void DeleteLastColumn<T>(this IMatrix<T> matrix);

    /// <summary>
    /// Deletes the last row from the matrix
    /// </summary>
    public static void DeleteLastRow<T>(this IMatrix<T> matrix);

    /// <summary>
    /// Gets the value at the specified position in the matrix
    /// </summary>
    public static T GetValueAt<T>(this IMatrix<T> matrix, int row, int column);

    /// <summary>
    /// Multiplies this matrix with the other matrix and returns the result
    /// </summary>
    public static IMatrix<T> Multiply<T>(this IMatrix<T> matrix, IMatrix<T> other, IVectorOperators<T> vectorOperators, IScalarOperators<T> scalarOperators)
    {
        JoinRowColumn<T> joiner = new JoinRowColumn<T>();
        return joiner.Join(matrix, other, vectorOperators.OperatorAdd, scalarOperators.OperatorMultiply);
    }

    /// <summary>
    /// Gets the row at the specified position
    /// </summary>
    public static IList<T> RowAt<T>(this IMatrix<T> matrix, int position);

    /// <summary>
    /// Gets the number of rows in the matrix
    /// </summary>
    public static int RowCount<T>(this IMatrix<T> matrix);

    /// <summary>
    /// Sets the number of rows in the matrix
    /// </summary>
    public static void RowCount<T>(this IMatrix<T> matrix, int rows);
}

Consider the Multiply method. The result of multiplying to IMatrix objects is well known. For simplicity, consider only the integer implementation of the Matrix. In order to calculate the result, we do not need to know anything about the matrix other than how Multiply(int, int) and Add(int, int) work. Since both of them are known, I should not need anything else to return a new matrix with that result. However, I'm unsure of the best way of doing this.

My approach was to add in the two methods FromRows and FromColumns to the interface. This seems wrong since I should not force construction of the matrix in this specific way (or so I feel). However, it's the only way that I could figure out how to return an instance of this interface. I would build the matrix in the joiner class using IList and ensure that the collection was either the row or column definition and then use the FromRows method. Perhaps this will make more sense with an example:

/// <summary>
/// Class used for joining by combining rows and columns
/// </summary>
/// <typeparam name="T">
/// Type of the values contained in the matrix
/// </typeparam>
class JoinRowColumn<T> : IJoinMatrix<T>
{
    public IMatrix<T> Join(IMatrix<T> a, IMatrix<T> b, IOperateVector<T> vectorOperation, IOperateScalar<T> cellOperation)
    {
        // ensure that the matricies can be joined
        if (a.ColumnCount() != b.RowCount())
        {
            throw new ArgumentException("Cannot join matricies.  Invalid dimensions");
        }

        IList<IList<T>> rowDefinition = IMatrixHelpers.GetRowDefinition<T>(a.RowCount(), b.ColumnCount());
        for (int row = 0; row < a.RowCount(); row++)
        {
            IList<T> aRow = a.RowAt(row);
            for (int col = 0; col < b.ColumnCount(); col++)
            {
                IList<T> bCol = b.ColumnAt(col);
                rowDefinition[row][col] = vectorOperation.Operate(aRow, bCol, cellOperation);
            }
        }
        // I do not like this because it is unclear that the
        // method is returning a NEW instance of IMatrix<T>
        // based on the row definition.  It does not update
        // a to contain the matrix defined by rowDefinition
        return a.FromRows(rowDefinition); // UGLY!
    }
}

So at the end of the method, I use one of the Matrices given to me to spawn a new matrix of the (probably) same type (though there is no restriction on what the matrix returns as far as the concrete implementation). There is part of the problem; the FromRows returns a NEW instance. It is not obvious, however and one may think that it is updating the matrix that the method is being called on.

Is there a better pattern to follow for adding in a way of constructing a concrete implementation of an interface? Or does this method seem OK?

I'm just getting familiar with generics so please bear with me if I'm not seeing something obvious.

Upvotes: 0

Views: 83

Answers (1)

Femaref
Femaref

Reputation: 61427

  • Include a method named Construct(int xDimension, int yDimension) on your interface, returning a new instance of it
  • Design a default implementation you use in such a case. As you are coding against interfaces, nobody should assume a specific implementation.

Personally, I would go with the second option. You are coding against interfaces anyway, the implementation isn't supposed to matter. You are easily able to return your default implementation of a matrix and the caller will be able to work with it. Aditionally, you should think about adopting this for your other methods as well - instead of manipulating the passed in matrix, create a new one and manipulate that.

This would be similar to the way LINQ works, and would prevent bugs to sneak in on the way. If you want to manipulate the current object, you don't need extension methods.

Upvotes: 1

Related Questions