How do you write code whose logic is protected against future additional enumerations?

后端 未结 10 1529
旧巷少年郎
旧巷少年郎 2021-02-05 08:01

I\'m having a hard time describing this problem. Maybe that\'s why I\'m having a hard time finding a good solution (the words just aren\'t cooperating). Let me explain via cod

10条回答
  •  时光说笑
    2021-02-05 08:53

    You're asking the wrong questions. You're looking for coding work arounds for what is inadequate design. You want to add to your enumeration with minimum hassle.

    I like your idea for using an enum for fruit types, but I'd have that as a field in a Fruit class.

    I would use a class rather than an interface. You want to capture the concept of a fruit. It's a concrete thing. OTOH an interface would be good if you wanted to add the behavior or "quality" of being "fruitable". You want to have lots of different types of "Fruit" (a class), you are not adding "fruit-ability" (an interface) to an otherwise non-fruit thingy.

    Rather than having a base "Fruit" class and sub-classing for every kind of fruit, just have an instance variable for the type - and make it an enumeration. Otherwise the number of sub-classes can get out of hand. Now every kind is a "Fruit". The "type" field tells us what kind.

    Now that we have the fundamental idea of a Fruit class, add the peel/core idea as another field. If there are only these two choices maybe the field could be a boolean, "isPeelable". If there where, or could be in the future, other choices like "smash" or "pluck", now an enumeration might be a good idea, just like it is for the fruit type field. I suppose the class' instance field might be called "prepToEat"?

    From here it gets interesting. Your design needs to be flexible to accommodate new fruit types. Also, it looks like you have a method for each "prepToEat" value. And we shall have none of that "exception coding" crap you describe in #1, #2 above.

    Because each fruit has several dynamic parts we will create a factory class. This puts all the details for making all the different kinds - and more importantly, future code changes - into one class.

    A KEY DESIGN ELEMENT is using a delegate for the "prepToEat" method. This again, prevents us from having to modify the Fruit class directly when we add fruits to our repitore.

      public class FruitEater
      {
         ArrayList myFruit;
         FruitFactory myFruitMaker;
    
         public FruitEater()
         {
            this.myFruit = new Arraylist();
            this.myFruitMaker = new FruitFactory();
         }
    
         public static void Main( args[] stuff )
         {
            myFruit.Add( myFruitMaker.Create( FruitType.Orange ));
            myFruit.Add( myFruitMaker.Create( FruitType.Apple ));
    
            foreach ( Fruit a in myFruit )
            {
               a.eat(); //FINALLY!!!!
            }
         }
    
      } //FruitEater class
    
    
    
      public class Fruit
      {
         public delegate void PrepareToEatDelegate();
    
         protected FruitType type;
         protected PrepType prepType;
         // pretend we have public properties to get each of these attributes
    
    
         // a field to hold what our delegate creates.
         private PrepareToEatDelegate prepMethod;
    
         // a method to set our delegate-created field
         public void PrepareToEatMethod( PrepareToEatDelegate clientMethod )
         {
            prepMethod = clientMethod;
         }
    
         public void Eat()
         {
            this.prepMethod();
            // do other fruit eating stuff
         }
    
          public Fruit(FruitType myType )
          {
            this.type = myType;
          }
      }
    
      public class FruitFactory
      {
         public FruitFactory() { }
    
         public Fruit Create( FruitType myType )
         {
            Fruit newFruit = new Fruit (myType);
    
            switch ( myType )
            {
               case FruitType.Orange :
                  newFruit.prepType = PrepType.peel;
                  newFruit.PrepareToEatMethod(new Fruit.PrepareToEatDelegate(FruitFactory.PrepareOrange));
                  break;
    
               case FruitType.Apple :
                  newFruit.prepType = PrepType.core;
                  newFruit.PrepareToEatMethod( new Fruit.PrepareToEatDelegate( FruitFactory.PrepareApple ) );
                  break;
    
               default :
                  // throw an exception - we don't have that kind defined.
            }
            return newFruit;
         }// Create()
    
         // we need a prep method for each type of fruit this factory makes
         public static void PrepareOrange()
         {
            // whatever you do
         }
    
         public static void PrepareApple()
         {
            // apple stuff 
         }
      }// FruitFactory
    
      public enum FruitType
      {
         Orange
         ,Apple
         ,Grape
      }
    
    
      public enum PrepType
      {
         peel
         ,core
         ,pluck
         ,smash
      }
    

提交回复
热议问题