-1
\$\begingroup\$

I am new to using interfaces and abstract classes. The team I am working with came up with an implementation of what they call a base class that will be implemented by all view model classes. This is what they came up with:

    public interface IBaseViewModel<T>
{
    #region public properties
    string Message { get; set; }
    string SaveType { get; set; }
    bool IsVisibleMessage { get; set; }
    bool IsVisibleDecision { get; set; }
    bool IsApiSuccess { get; set; }
    Guid EntityId { get; set; }
    string EntityQDSId { get; set; }
    string EntityType { get; set; }
    #endregion

    #region public methods
    Task Init();
    Task Init(Guid Id);
    Task<T> Create();
    Task<T> Get(Guid Id);
    Task<T> Get(string Id);
    Task<IEnumerable<T>> GetList(Guid Id);
    Task<IEnumerable<T>> GetList(string Id);
    Task<T> Insert(T t);
    Task<T> Update(Guid Id, T t);
    Task<T> Delete(Guid Id);
    void Dispose();
    #endregion
}

public abstract class BaseViewModel<T> : IBaseViewModel<T>, IDisposable
{
    public Guid EntityId { get; set; }
    public string EntityQDSId { get; set; }
    public string EntityType { get; set; }

    public string Message { get; set; }
    public string SaveType { get; set; }
    public bool IsVisibleMessage { get; set; }
    public bool IsVisibleDecision { get; set; }
    public bool IsApiSuccess { get; set; }

    public virtual Task Init()
    {
        return Task.FromResult<T>(default);
    }

    public virtual Task Init(Guid Id)
    {
        return Task.FromResult<T>(default);
    }

    public virtual Task<T> Create()
    {
        return Task.FromResult<T>(default);
    }

    public virtual Task<T> Get(Guid Id)
    {
        return Task.FromResult<T>(default);
    }

    public virtual Task<T> Get(string Id)
    {
        return Task.FromResult<T>(default);
    }

    public virtual Task<IEnumerable<T>> GetList(Guid Id)
    {
        return Task.FromResult<IEnumerable<T>>(default);
    }

    public virtual Task<IEnumerable<T>> GetList(string Id)
    {
        return Task.FromResult<IEnumerable<T>>(default);
    }

    public virtual Task<IEnumerable<T>> GetForGrid(Guid Id)
    {
        return Task.FromResult<IEnumerable<T>>(default);
    }

    public virtual Task<T> Insert(T t)
    {
        return Task.FromResult<T>(default);
    }

    public virtual Task<T> Update(Guid Id, T t)
    {
        return Task.FromResult<T>(default);
    }

    public virtual Task<T> Delete(Guid Id)
    {
        return Task.FromResult<T>(default);
    }

    public virtual void ProcessDto(T t)
    {
    }

    public virtual void Dispose()
    {
    }
}

  public interface IAccountBillViewModel<T> : IBaseViewModel<AccountBillGroup>
{

}

Below is the implementation:

 public class AccountBillViewModel<T> : BaseViewModel<AccountBillGroup>, IAccountBillViewModel<AccountBillGroup>
{

}

In researching this question I found several comments like the one below

https://stackoverflow.com/a/479154 [@Alex][1]

My points:

Since there was only code signatures and no implementations of any code in the abstract class and because you can inherit from multiple interfaces, why use an abstract class instead of a interface? The inheritance slot will be left open in case we wanted to inherit from another class in the future. Their points:

The properties only need to be implemented in the base class and those properties will be available in all implementations without declaring the properties again. Only those methods that need to be used in the implementation need to be overridden. If an interface was used all the methods would have to be implemented in every implementation. Is there value in choosing to go with an abstract base class when there is not implementations, only method signatures?

\$\endgroup\$
2
  • 3
    \$\begingroup\$ Rather than a review of the existing code, you appear to be looking for a discussion around the relative merits of interfaces Vs abstract base classes... \$\endgroup\$
    – forsvarir
    Oct 26 '21 at 13:15
  • \$\begingroup\$ "Since there was only code signatures and no implementations of any code in the abstract class and because you can inherit from multiple interfaces, why use an abstract class instead of a interface?" This question used to be easy to answer (abstract classes can have default implementations), but since the advent of default implementations being allowed on interfaces, I get the feeling that the distinction between abstract classes and interfaces has mostly faded, in favor of interfaces. I have not found a conventionally agreed upon update to this advice since this feature has been introduced. \$\endgroup\$
    – Flater
    Oct 26 '21 at 13:26
1
\$\begingroup\$

The implementation must not have the generic type parameter T, because it is implementing BaseViewModel<T> with the concrete type AccountBillGroup supplemented for T. The same holds for the interface IAccountBillViewModel.

public interface IAccountBillViewModel : IBaseViewModel<AccountBillGroup>
{
}

public class AccountBillViewModel : 
    BaseViewModel<AccountBillGroup>, IAccountBillViewModel
{
}

So, what the interface IAccountBillViewModel and the class AccountBillViewModel really do, is to nail down the generic type parameter AccountBillGroup.

Now, in the class AccountBillViewModel the methods are implemented with all the Ts substiuted by AccountBillGroup

public virtual Task<AccountBillGroup> Insert(AccountBillGroup t)
{
    return Task.FromResult<AccountBillGroup>(default);
}

You don't see this implementation which is done automatically in the background, but IntelliSense will show you the signatures of these methods with this substitution applied.

The T in class AccountBillViewModel<T> is unrelated to the T in the interface and introduces a new type parameter which remains unused in your implementation. Therefore, e.g., AccountBillViewModel<int> would still implement BaseViewModel<AccountBillGroup>! T is not only obsolete, it is misleading and wrong.


Since there was only code signatures and no implementations of any code in the abstract class ...

This is a wrong statement. The abstract class does implement the properties and the methods. If it wouldn't, these members would have to be marked as abstract.

public abstract class BaseViewModel<T> : IBaseViewModel<T>, IDisposable
{
    public abstract string Message { get; set; }

    public abstract Task Init();

    ...
}

The properties are implemented as auto-properties. This means that the compiler is providing the implementation.

// In a class this ...
public string Message { get; set; }

// ...is equivalent to
private string _message;
public string Message {
    get { return _message; }
    set { _message = value; }
}

Some of the methods have an empty body. This does not mean that they are not implemented. It means that the implementation consists of doing nothing. E.g. it makes sense to implement void Dispose() with an empty body, when there is nothing to be disposed.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.