0

複数の要因に基づいてアプリの動作を決定するビジネス ロジックがたくさんあるとします。また、動作を戦略パターンに置き換えることができる、非常に優れた場所がいくつかあります。また、問題を解決するためにさまざまなパターンを活用しようとしていると考えると、

  • 戦略パターン
  • 仕様パターン
  • 工場パターン

工場で戦略を決定するために仕様パターンを使用し、Open Closed 原則を維持することは可能ですか?

私は工場を持っており、正しい戦略を選択するために、前のコードと同じように switch ステートメントを作成していることに気づきました。これは逆効果のようです。

これらすべての論理的な決定を仕様に押し付けたいのですが、仕様を順序付けするか、最も明確な仕様を最初に選択するかという問題が発生します。

これを解決する方法はありますか?

4

1 に答える 1

0
using System;
using System.Collections.Generic;
using System.Linq;

using static BeyondOOP.Gender;

// having to make decisions based on logic
// control flow without all the nested if then
namespace BeyondOOP
{
    class Program
    {
        static void Main(string[] args)
        {
            var people = new List<Person>
            {
                new Person("Bill", new DateTime(1940, 10, 10), Male),
                new Person("Jill", new DateTime(1950, 10, 10), Female),
                new Person("Mary", new DateTime(2015, 10, 10), Female),
                new Person("Gary", new DateTime(1970, 10, 10), Male),
                new Person("Greg", new DateTime(1980, 10, 10), Male),
                new Person("Susan", new DateTime(2013, 10, 10), Female),
                new Person("Gabe", new DateTime(1999, 10, 10), Neutral),
                new Person("Barbie", new DateTime(2000, 10, 10), Female),
            };
            var greeter = new PersonGreeter();
            people.ForEach(greeter.Greet);
        }
    }

    // a little 'generics' flair
    // a 'predicate' is just a way
    // to predetermine true false
    interface IPredicate<T>
    {
        bool Matches(T value);
    }

    // this is just a simple DTO
    // Data transfer object, fancy for value container
    // it's just a way of passing values around
    class Person
    {
        public string Name { get; }
        public DateTime DateOfBirth { get; }
        public Gender Gender { get; }

        public Person(string name, DateTime dateOfBirth, Gender gender)
        {
            Name = name;
            DateOfBirth = dateOfBirth;
            Gender = gender;
        }
    }

    enum Gender { Male, Female, Neutral }

    // some prefabed predicates for 'Person'
    class OldManPredicate : IPredicate<Person>
    {
        public bool Matches(Person value) =>
            (DateTime.Now.Year - value.DateOfBirth.Year) >= 60 && 
                value.Gender == Gender.Male;
    }

    class MinorPredicate : IPredicate<Person>
    {
        public bool Matches(Person value) =>
            DateTime.Now.Year - value.DateOfBirth.Year < 18;
    }

    class ToddlerPredicate : IPredicate<Person>
    {
        public bool Matches(Person value) =>
            DateTime.Now.Year - value.DateOfBirth.Year > 2 &&
            DateTime.Now.Year - value.DateOfBirth.Year < 4;
    }

    class BabyPredicate : IPredicate<Person>
    {
        public bool Matches(Person value) =>
            DateTime.Now.Year - value.DateOfBirth.Year <= 2;
    }

    class TruePersonPredicate : IPredicate<Person>
    {
        public bool Matches(Person value) => true;
    }

    class GreetAction
    {
        public Func<Person,bool> Predicate { get; }
        public Func<Person,string> Messager { get; }

        public GreetAction(Func<Person, bool> predicate, 
            Func<Person, string> messager  )
        {
            Predicate = predicate;
            Messager = messager;
        }
    }
    class PersonGreeter
    {
        // someone may say that if you're using Func
        // why do you go through the trouble of the extra
        // classes. Predicate classes can be added freely
        // Only this factory class needs to be changed
        // and if I was to create a factory for the predicates 
        // and messagers then no changes would be neccessary
        private readonly List<GreetAction> _greetActions =
            new List<GreetAction>
            {
                // these have to be ordered
                // so they don't conflict or override
                // intended behavior
                // MinorPredicate is < 18 but babies and toddlers are also
                // less than 18, they need to preceed minors to work
                new GreetAction( new OldManPredicate().Matches, 
                    p => $"{p.Name}, you're getting up there!"),
                new GreetAction( new BabyPredicate().Matches,
                    p => $"{p.Name}, time to change the diaper!"),
                new GreetAction( new ToddlerPredicate().Matches,
                    p => $"{p.Name}, look, you're walking!"),
                new GreetAction( new MinorPredicate().Matches, 
                    p => $"{p.Name}, no, you may not have beer!"),
                // you need a default action
                // this is the same as a 'Null Pattern'
                new GreetAction( new TruePersonPredicate().Matches,
                    p => $"{p.Name}, hello there!"),
            }; 

        public void Greet(Person person) => 
            Console.WriteLine(_greetActions
                .First(p => p.Predicate(person))
                .Messager(person));
    }
}
于 2016-03-10T22:20:04.127 に答える