سؤال

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