Interface Segregation principle

As Martin states, this principle deals with the inconveniences of "fat" interfaces. And the problem arises when the interfaces of the class can be logically fragmented into distinct groups or methods.

In this case, if there is more than a client of our application, chances are that some clients are connected to a functionality they never use.

Back to our demo again: the mere review of the definition reveals that our system has some defects from the point of view of this principle.

First, we're implementing a method that is only used by a type of a SportCar client: the Mercedes. The other brands don't use it. In case a new condition arises for a different brand, new options should be created.

So, this marks a difference in the way in which we can categorize our cars: those who notify the user interface about SpeedLimit and those who don't. We should start by redefining our ISportCar interface to cover only those aspects that are commonly used by any client. This includes the LegalLimitCondition event but not the SpeedLimit event.

So, we will have this implementation:

interface ISportCar
{
  bool Accelerate();
  System.Drawing.Bitmap Photo { get; }
  string Brand { get; }
  int Speed { get; }
  int MaxSpeed { get; }
  eventEventHandler<int> LegalLimitCondition;
}

The new version of SportCar would implement only an Accelerate overload of the method, launching the LegalLimitCondition event but not the SpeedLimit event, which is only suitable for the Mercedes:

public virtualbool Accelerate()
{
  bool speedExceeded = Speed + SpeedIncr > MaxSpeed;
  bool legalExceeded = Speed + SpeedIncr > MaxLegal;
  if (legalExceeded && LegalLimitCondition != null)
  {
    LegalLimitCondition(this, (Speed + SpeedIncr) - MaxLegal);
  }
  Speed = (speedExceeded) ? Speed: Speed + SpeedIncr;
  return speedExceeded;
}

Note that we still control MaxSpeed, only that we don't take any action but avoid the speed beyond the maximum value.

This separation suggested by this principle also applies to the first principle, since now, the responsibilities of this class are focused on the group of clients that use this implementation.

On the other hand, we will create a new class SportsCarWithN (a sports car with notifications) that inherits from SportsCar but adds the functionality required by the Mercedes (or any other brand that would decide to do this in the future):

public class SportsCarWithN : SportsCar, ISportCar
{
  public SportsCarWithN(string brand): base(brand) {}
  public new bool Accelerate()
  {
    base.Accelerate();
    bool speedExceeded = Speed + SpeedIncr > MaxSpeed;
    if (speedExceeded && (SpeedLimit!= null))
    {
      SpeedLimitData data = new SpeedLimitData()
      {
        moment = DateTime.Now,
        resultingSpeed = Speed + SpeedIncr
      };
      SpeedLimit(this, data);
    }
    Speed = (speedExceeded) ? Speed : Speed + SpeedIncr;
    return speedExceeded;
  }
  public event EventHandler SpeedLimit;
}

In this manner, each part of the hierarchy takes care of its own duties. Any car that inherits from SportCarWithN will have the extra functionality, while the rest of the cars will behave in the standard manner.

In the user interface, things also get simplified. Now, we declare theCar to be of type ISportCar and decide which constructor to call at execution time:

ISportCar theCar;
private void cboPickUpCar_SelectedIndexChanged(object sender, EventArgs e)
{
  if (cboPickUpCar.Text == "Mercedes")
  {
    theCar = new SportsCarWithN("Mercedes");
    // subscription to SpeedLimit depends on type
    ((SportsCarWithN)theCar).SpeedLimit += TheCar_SpeedLimit;
  }
  else
  {
    theCar = new SportsCar(cboPickUpCar.Text);
  }
  theCar.LegalLimitCondition += TheCar_LegalLimitCondition;
  // refresh car's properties
  txtMaxSpeed.Text = theCar.MaxSpeed.ToString();
  pbPhoto.Image = theCar.Photo;
  updateUI();
}

The btnAccelerate_Click event handler is also simplified, since every instance of ISportCar will know how to call the appropriate method in the underlying model:

private void btnAccelerate_Click(object sender, EventArgs e)
{
  theCar.Accelerate();
  updateUI();
}

Now, at runtime, only the Mercedes brand receives both notifications, while the rest of the brands get only the LegalLimitCondition event.

You can check the results in Demo-ISP and check out both types of conditions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.44.182