1. Extension and reuse problems caused by inheritance

As one of the three elements of object-oriented (encapsulation, inheritance, polymorphism) why inheritance is a problem, and how the problem can be solved to form a design pattern, head Frist design Pattern book uses the duck as an example to explain when inheritance is a problem. First there are all kinds of ducks, so all kinds of ducks inherit from one parent class: the parent class is Duck, and there are GreenHeadDuck and RedHeadDuck

public abstractClass Duck{public void quack(){}public void swin(){}public abstract void display();
}
Copy the code
publci class GreenHeadDuck:Duck{
    public overrid void  display(){
      // Look green}}Copy the code
publci class ReadHeadDuck:Duck{
    public overrid void  display(){
      // Red in appearance}}Copy the code

All the ducks in the superclass quack and swin, but look different. So display is an abstract method, and let the subclass that inherits it override it. Now we need to add a new kind of duck, but this duck is a toy RubberDuck, we follow the inheritance method of RubberDuck implementation code is as follows

publci class RubberDuck:Duck{public override void qucak(){// Cover into a squeak}public override void  display(){
      // Look like a rubber duck}}Copy the code

Since the rubber duck does not quack like other ducks, we need to override the qucak method to override the way the rubber duck quacks. Now we have a requirement to make the ducks fly. We can easily make all the ducks fly by adding the fly method to the superclass Duck in accordance with the inheritance design. The problem is that rubber ducks can’t fly, so we can override the fly method in RubberDuck just like we override the Qucak method.

public abstract class Duck{public void quack(){}public void swin(){}public abstract void display();
    public void fly(){}
}

publci class RubberDuck:Duck{public override void qucak(){// Cover into a squeak}public override void  display(){
      // Look like a rubber duck
    }
    public override void fly(){
        // Overlay, do nothing}}Copy the code

Here we have discovered the problems of inheritance:

1. Code is repeated in multiple subclasses.

2. It is difficult to know the behavior of all subclasses.

3. Running subclass behavior is not easy to change.

Change will pull the whole body, causing other subclasses do not want to change.

Whenever a new subclass appears, check to see if the superclass method needs to be overridden. For example, add a wooden toy duck DecoyDuck, so the wooden duck can neither quack nor fly, do we need to cover the way of calling and flying?

2. Further improvements, using interfaces

Since quack and fly can change, we abstract quack and fly into interfaces. Ducks that can call and fly inherit the interfaces and implement their own methods as required.Part of the problem can be solved by using interfaces (You no longer need to override unwanted methods), but it makes the code unreusable because the interface doesn’t have an implementation, so we have to write fly and quack in each subclass. Like “quack” for red and green, or “squeak” for rubber duck.

3. Further improvement, strategic mode

The above analysis leads to two principles of design patterns

1. Encapsulate the parts that will change so that other parts will not be affected.

2. Program for interfaces, not implementations.

The first design principle allows us to take out the parts that are easy to change: the fly behavior and the quack behavior of the duck. We know from the second design principle that each behavior needs to be represented by an interface, such asFlyBehaviorwithQuackBehavior, and each implementation of the behavior will implement one of the interfaces. So the duck class is not responsible for the implementationFlyBehaviorwithQuackBehaviorInstead, it is implemented exclusively by the behavior class, which is not tied to the subclass of duck.And “Programming to interfaces” means “programming to supertypes”. We can explicitly state that the declaration type of the variable should be superclass, which means that the behavior variable we declare in the Duck superclass isFlyBehavior.QuackBehaviorThe key to “programming to an interface” is polymorphism, one of the three elements of object orientation. Because of polymorphism, when we call a method of the superclass, we execute a method of the implementing class or subclass. So we rewrote what we wrote beforeDuckClass, remove the Fly() and Quack() methods, and addFlyBehaviorandQuackBehaviorVariable, and two other methods, PerFormFly() and PerFormQuack(), are used to perform the two actions.

public abstract Class Duck{
    protected FlyBehavior flyBehavior;
    protectedQuackBehavior quackBehavior;public void swin(){}public abstract void display();public void PerFormFly(){ flyBehavior.fly(); }public void PerFormQuack(){ quackBehavior.quack(); }}Copy the code

Write related classes and test them:

// Encapsulate the flight behavior
public interface FlyBehavior
 {
     public void fly();
 }

 public class FlyWithWings : FlyBehavior
 {
     public void fly()
     {
         Console.WriteLine("Fly with wings."); }}public class FlyNoWay : FlyBehavior
 {
     public void fly()
     {
         Console.WriteLine("No flying, no doing."); }}Copy the code
// Encapsulate the call behavior
public interface QuackBehavior
{
   public void quack();
}

public class Quack : QuackBehavior
{
    public void quack()
    {
        Console.WriteLine("Quack."); }}public class Squack : QuackBehavior
{
    public void quack()
    {
        Console.WriteLine("Squeak"); }}public class MuteQuack : QuackBehavior
{
    public void quack()
    {
        Console.WriteLine("Can't bark."); }}Copy the code
/// <summary>
///The duck superclass
/// </summary>
public abstract class Duck {

    protected FlyBehavior flyBehavior;
    protected QuackBehavior quackBehavior;

    public void swin(){}public abstract void display();
    public void PerFormFly()
    {
        flyBehavior.fly();
    }
    public void PerFormQuack(){ quackBehavior.quack(); }}/// <summary>
///Mallard ducks
/// </summary>
public class GreenHeadDuck : Duck
{
    public GreenHeadDuck() {
        flyBehavior = new FlyWithWings();
        quackBehavior = new Quack();
    }
    public override void display()
    {
        Console.WriteLine("Mallard, there is a meadow above my head (*^_^*)"); }}/// <summary>
///Rubber ducks
/// </summary>
public class RubberDuck : Duck
{
    public RubberDuck()
    {
        flyBehavior = new FlyNoWay();
        quackBehavior = new Squack();
    }
    public override void display()
    {
        Console.WriteLine("Rubber duck"); }}Copy the code
static void Main(string[] args)
 {
     / / mallard ducks
     Duck greenHeadDuck = new GreenHeadDuck();
     greenHeadDuck.display();
     greenHeadDuck.PerFormQuack();
     greenHeadDuck.PerFormFly();
     Console.WriteLine("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
     / / rubber duck
     Duck rubberDuck = new RubberDuck();
     rubberDuck.display();
     rubberDuck.PerFormQuack();
     rubberDuck.PerFormFly();
     Console.ReadKey();
 }
Copy the code

As shown above, we instantiate the behavior class in the constructor of the duck subclass, but we don’t use a bunch of dynamic functions, so we can set the behavior dynamically instead of instantiating it in the constructor. So we can also add two behavior setting methods, SetFlyBehavior and SetQuackBehavior, to test the dynamic behavior again after the instantiation of fly and call.

    /// <summary>
    ///The duck superclass
    /// </summary>
    public abstract class Duck {

        protected FlyBehavior flyBehavior;
        protected QuackBehavior quackBehavior;

        public void swin(){}public abstract void display();

        public void SetFlyBehavior(FlyBehavior flyBehavior) {
            this.flyBehavior = flyBehavior;
        }

        public void SetQuackBehavior(QuackBehavior quackBehavior) {
            this.quackBehavior = quackBehavior;
        }
        public void PerFormFly()
        {
            flyBehavior.fly();
        }
        public void PerFormQuack(){ quackBehavior.quack(); }}Copy the code

4. Summary of policy pattern class diagram

Strategy pattern: A cluster of algorithms is defined and encapsulated so that they are interchangeable with each other. This pattern allows the algorithm to change independently of the customers using the algorithm

With this class diagram, you can easily understand and remember the design patterns, and then share your learning and understanding with other patterns.