문제

I have read some examples of the specification pattern, but it is hard to get idea how to implement with this pattern.

I am developing a huge program for a client. I need to import XML files from a specific bank, then do validation with each of file. Ledger Codes has different methods (subs, bo, rcc). so when a file reads SUBS, it should send to a SUBS method.

Example here:

Interfaces:

  • BlackBank
  • BlueBank
  • RedBank

Ledger Codes:

  • SUBS
  • BO
  • RCC

Result:

  • BlackBank has SUBS, BO and RCC
  • BlueBank has SUBS
  • RedBank has BO and RCC

Can you please give some example code or point me in the right direction?

도움이 되었습니까?

해결책

Without context this is hard to answer so I will try making something up around the information I have and hopefully it'll give you an idea.

Create a simple specification interface like so

interface ISpecification<T>
{
    IsSatisfiedBy(T obj);
}

Pretending you have a base interface for 'banks' looking like

interface IBank
{
    LedgerCode LedgerCode { get; set; }
}

And an enum of LedgerCodes

[Flags]
enum LedgerCodes
{
    SUBS, BO, RCC
} 

You can make a simple ledger code specification to check the LedgerCodes of an IBank (this is quite general you need to make it specific to your needs)

class LedgerCodeSpec : ISpecification<IBank>
{
    private LedgerCode code;

    public LedgerCodeSpecification(LedgerCode code)
    {
        this.code = code
    }

    public override bool IsSatisfiedBy(IBank obj)
    {
        return obj.LedgerCode == code;
    }
}

Where appropriate you can use your specification, here I use it to provide simple validation. Another use is for 'selection' e.g. getting data from a repository

class Bank : IBank
{
    private ISpecification<IBank> spec;
    private LedgerCode code;

    public Bank(ISepcification<IBank> spec)
    {
        this.code = code;
        this.spec = spec;
    }

    public LedgerCode LedgerCode { get; set; }

    public bool IsValid 
    { 
        get
        {
            return spec.IsSatisfiedBy(this);
        }
    } 
}

And finally some code to quickly test/demonstrate the above

class Main
{
    public static void Main()
    {
        var spec = new LedgerCodeSpec(LedgerCodes.SUB)
        var blueBank = new Bank(spec);

        Console.WriteLine(blueBank.IsValid); // false

        blueBank.LedgerCode = LedgerCodes.RCC | LedgerCodes.SUB;

        Console.WriteLine(blueBank.IsValid); // false

        blueBank.LedgerCode = LedgerCodes.SUB;

        Console.WriteLine(blueBank.IsValid); // true
    }
}

There are some nice examples on the web adding extension methods and also overriding operators to give a succinct and imo more naturally readable spec e.g.

class MessageSpecification : Specification<string>
{
    public const int MIN_LENGTH = 5;
    public const int MAX_LENGTH = 60;

    public override bool IsSatisfiedBy(string s)
    {
        Specification<string> length = new LengthSpecification(MIN_LENGTH, MAX_LENGTH);
        Specification<string> isNull = new IsNullSpecification<string>();

        Specification<string> spec = length && !isNull;

        return spec.IsSatisfiedBy(s);
    }
}

the way i'm currently using the pattern is probably overkill but i like the idea of removing, reusing and generally making the logic more OO.

Edit: after reading some of the comments then your problem seems to be more related to a general dispatch problem rather then the specification pattern. given your interfaces you could more simply do.

class BankFacade
{
    public Send(IBlueBank bank)
    {
        // validate with specification
        // do stuff with IBlueBank
    }

    public Send(IRedBank bank)
    {
        // validate with specification
        // do stuff with IRedBank
    }

    //...
}

thinking some more may you could do something along the lines of

class Parser
{
    static class RedBankSpecification : ISpecification<XElement>
    {
        public override bool IsSatisfiedBy(XElement element)
        {
            return element.Value.equals("RED");
        }
    }

    public void Parse(XDocument doc)
    {
        var rspec = new RedBankSpecification();

        foreach(XElement e in doc)
        {
            if (r.IsSatisfiedBy(e))
            {
                IRedBank bank = new RedBank(e);
                bankFacade.Send(bank);
            }
        }

        //...
    }
}

however you may not really need the pattern and you shuoldn't try to shoehorn the problem into it

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top