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