Question

I consider interfaces not only as a set of members, but also as a "contract" which force realisation to hold restrictions specified in interface documentation. For example:

interface IDevice
{
    bool IsActive { get; }

    int Address { get; }

    /// <summary>
    /// Raised when (IsActive == false)
    /// and device was activated
    /// </summary>
    event Action Activated;

    /// <summary>
    /// Raised when (IsActive == true)
    /// and device was deactivated
    /// </summary>
    event Action Deactivated;

    /// <summary>
    /// Raised when (IsActive == false)
    /// and Address was changed
    /// </summary>
    event Action<int> AddressChanged;
}

Also I have users which are not interested in activation/deactivation process but want to know when Address will change, so, lead by ISP, I create a new interface:

interface IAddressee
{
    int Address { get; }

    /// <summary>
    /// Raised when Address was changed
    /// </summary>
    event Action<int> AddressChanged;
}

And now IDevice looks like:

interface IDevice : IAddressee
{
    bool IsActive { get; }

    /// <summary>
    /// Raised when (IsActive == false)
    /// and device was activated
    /// </summary>
    event Action Activated;

    /// <summary>
    /// Raised when (IsActive == true)
    /// and device was deactivated
    /// </summary>
    event Action Deactivated;
}

As you see, IDevice's contract has loosed one condition: AddressChanged event should be raised only when device is not active (IsActive == false).

I cannot document it in IAddressee interface since it is not depend on IDevice and non-device implementations can exist.

Is this situation is normal? What would you do to force IDevice realisation to correct behaviour?

I am new to the concept of contracts, so please dispel my illusions and doubts

Était-ce utile?

La solution

In such cases abstract conditions will do. They express something that may depend on information not available in the top-level class. The condition is later implemented in a way suitable for a specific descendant. In your example

interface IAddressee
{
    int Address { get; }

   /// <summary>
   /// Can Address be changed?
   /// </summary>
   bool IsAddessChangeable { get; };

    /// <summary>
    /// Raised when (IsAddessChangeable == true)
    /// and Address was changed
    /// </summary>
    event Action<int> AddressChanged;
}

In a class that implements IDevice, the query IsAddessChangeable will return IsActive == false, in other classes - the value depending on the required semantics.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top