Replace conditional with polymorphism refactoring or similar?

前端 未结 5 988
感动是毒
感动是毒 2021-02-06 00:45

I have tried to ask a variant of this question before. I got some helpful answers, but still nothing that felt quite right to me. It seems to me this shouldn\'t really be that h

5条回答
  •  难免孤独
    2021-02-06 01:19

    You don't need the Factory if your subclasses filter themselves on what they want to charge for. That requires a Project class to hold the data, if nothing else:

    class Project {
        TaskType Type { get; set; }
        int? NumberOfHours { get; set; }
    }
    

    Since you want to add new calculations easily, you need an interface:

    IProjectHours {
        public void SetHours(IEnumerable projects);
    }
    

    And, some classes to implement the interface:

    class AnalysisProjectHours : IProjectHours {
        public void SetHours(IEnumerable projects) {
           projects.Where(p => p.Type == TaskType.Analysis)
                   .Each(p => p.NumberOfHours += 30);
        }
    }
    
    // Non-LINQ equivalent
    class AnalysisProjectHours : IProjectHours {
        public void SetHours(IEnumerable projects) {
           foreach (Project p in projects) {
              if (p.Type == TaskType.Analysis) {
                 p.NumberOfHours += 30;
              }
           }
        }
    }
    
    class WritingProjectHours : IProjectHours {
        public void SetHours(IEnumerable projects) {
           projects.Where(p => p.Type == TaskType.Writing)
                   .Skip(0).Take(2).Each(p => p.NumberOfHours += 30);
           projects.Where(p => p.Type == TaskType.Writing)
                   .Skip(2).Take(6).Each(p => p.NumberOfHours += 20);
           projects.Where(p => p.Type == TaskType.Writing)
                   .Skip(8).Each(p => p.NumberOfHours += 10);
        }
    }
    
    // Non-LINQ equivalent
    class WritingProjectHours : IProjectHours {
        public void SetHours(IEnumerable projects) {
           int writingProjectsCount = 0;
           foreach (Project p in projects) {
              if (p.Type != TaskType.Writing) {
                 continue;
              }
              writingProjectsCount++;
              switch (writingProjectsCount) {
                  case 1: case 2:
                    p.NumberOfHours += 30;
                    break;
                  case 3: case 4: case 5: case 6: case 7: case 8:
                    p.NumberOfHours += 20;
                    break;
                  default:
                    p.NumberOfHours += 10;
                    break;
              }
           }
        }
    }
    
    class NewProjectHours : IProjectHours {
        public void SetHours(IEnumerable projects) {
           projects.Where(p => p.Id == null).Each(p => p.NumberOfHours += 5);
        }
    }
    
    // Non-LINQ equivalent
    class NewProjectHours : IProjectHours {
        public void SetHours(IEnumerable projects) {
           foreach (Project p in projects) {
              if (p.Id == null) {
                // Add 5 additional hours to each new project
                p.NumberOfHours += 5; 
              }
           }
        }
    }    
    

    The calling code can either dynamically load IProjectHours implementors (or static them) and then just walk the list of Projects through them:

    foreach (var h in AssemblyHelper.GetImplementors()) {
       h.SetHours(projects);
    }
    Console.WriteLine(projects.Sum(p => p.NumberOfHours));
    // Non-LINQ equivalent
    int totalNumberHours = 0;
    foreach (Project p in projects) {
       totalNumberOfHours += p.NumberOfHours;
    }
    Console.WriteLine(totalNumberOfHours);
    

提交回复
热议问题