c# - सशर्त बिल्डर विधि चेनिंग फ्लुएंट इंटरफेस




design-patterns builder (3)

आप विद के अधिभारित संस्करणों को लिखने पर विचार कर सकते हैं, और दूसरे में, एक तर्क के रूप में कहां लें:

var level = 5;  
var ninja = NinjaBuilder     
     .CreateNinja()
     .Named("Ninja Boy")
     .AtLevel(level)
     .WithShurikens(10)
     .WithSkill(Skill.HideInShadows, Where.Level(l => l > 3))
     .Build() 

बेशक, यह इस धारणा पर आधारित है कि आप लिखने जा रहे हैं, जहां एक अलग वस्तु पूरी तरह से है, यह अनिवार्य रूप से इस तरह दिखता है:

public sealed static class Where
{
    public bool Defense (Func<int, bool> predicate) { return predicate(); }
    public bool Dodge (Func<int, bool> predicate) { return predicate(); }
    public bool Level (Func<int, bool> predicate) { return predicate(); }

}

मैं सोच रहा था कि एक कार्यान्वयन का सबसे अच्छा तरीका क्या होगा .When एक Builder ऑब्जेक्ट में विधि श्रृंखला का उपयोग कर एक धाराप्रवाह इंटरफ़ेस में स्थिति?

उदाहरण के लिए मैं निम्नलिखित उदाहरण में .WithSkill() और .WithSkill() .When() विधियों को कैसे लागू .WithSkill() :

var level = 5;

var ninja = NinjaBuilder
    .CreateNinja()
    .Named("Ninja Boy")
    .AtLevel(level)
    .WithShurikens(10)
    .WithSkill(Skill.HideInShadows)
        .When(level > 3)
    .Build()

अद्यतन - यहां एक नमूना समाधान पाया जा सकता here


आपके पास आपकी विधि में एक सशर्त वैकल्पिक पैरामीटर हो सकता है जो डिफ़ॉल्ट रूप से true है:

.WithSkill(Skill.HideInShadows, when: level > 3)

यह निश्चित रूप से WithSkill विधि के लिए बहुत विशिष्ट होगा:

public NinjaBuilder WithSkill(Skill skill, bool when = true) {
  if (!when) return this;
  // ...
}

आप इसे अन्य तरीकों से जोड़ सकते हैं जिन्हें आप सशर्त भी बनाना चाहते हैं।

एक और विकल्प यह है कि एक ऐसी विधि हो जो बिल्डर के सशर्त भागों को घोंसला दे:

public NinjaBuilder When(bool condition, Action<NinjaBuilder> then) {
  if (condition) then(this);
  return this;
}

फिर आप इसे इस तरह लिख सकते हैं:

.When(level > 3, 
  then: _ => _.WithSkill(Skill.HideInShadows))

या इस तरह:

.When(level > 3, _=>_
  .WithSkill(Skill.HideInShadows)
)

यह अधिक सामान्य है और निर्माता के किसी भी तरीके से इसका उपयोग किया जा सकता है।

आप एक वैकल्पिक "अन्य" भी जोड़ सकते हैं:

public NinjaBuilder When(bool condition, Action<NinjaBuilder> then, Action<NinjaBuilder> otherwise = null) {
  if (condition) {
    then(this);
  }
  else if (otherwise != null) {
    otherwise(this);
  }
  return this;
}

या, "mixin" :

public interface MBuilder {}
public static class BuilderExtensions {
  public static TBuilder When<TBuilder>(this TBuilder self, bool condition, Action<TBuilder> then, Action<TBuilder> otherwise = null)
  where TBuilder : MBuilder
  {
    if (condition) {
      then(self);
    }
    else if (otherwise != null) {
      otherwise(self);
    }
    return self;
  }
}

public class NinjaBuilder : MBuilder ...

यह निश्चित रूप से विधि कॉल के रूप में "if" कथन बनाने का एक तरीका है। अन्य तरीके भी काम कर सकते हैं:

.When(level > 3) // enter "conditional" context
  .WithSkill(Skill.HideInShadows)
.End() // exit "conditional" context

इस मामले में, बिल्डर ट्रैक रखता है कि क्या स्थिति को गलत होने पर "सशर्त" संदर्भ में किए गए किसी भी विधि कॉल को अनदेखा करना चाहिए या नहीं। संदर्भ When दर्ज करेंगे, End इसे बाहर निकल जाएगा। आप "अन्य" संदर्भ को चिह्नित करने के लिए Otherwise() कॉल भी कर सकते हैं। दिलचस्प बात यह है कि आप इस तरह के अन्य बयान भी कवर कर सकते हैं, जैसे लूप:

.Do(times: 10) // add 10 shurikens
  .AddShuriken()
.End()

इस मामले में, "लूप" संदर्भ में किए गए कॉल रिकॉर्ड किए जाने चाहिए और End में वांछित संख्या को खेला जाना चाहिए जब End कहा जाता है।

तो, संदर्भ एक प्रकार का राज्य है जिस पर बिल्डर हो सकता है; वे बदलते हैं कि यह कैसे काम करता है। आप ट्रैक रखने के लिए एक ढेर का उपयोग करके, संदर्भों को घोंसला भी कर सकते हैं। और आपको यह जांचना चाहिए कि कुछ राज्यों में कुछ कॉल मान्य हैं या नहीं, अगर वे नहीं हैं तो अपवाद फेंक दें।


मैं क्या करूँगा NinjaBuilder को उनकी NinjaBuilder बजाए प्रतिनिधियों की एक सूची के रूप में रखता है, और केवल उन्हें लागू करते हैं .Build को बुलाया जाता है। यह आपको सशर्त बनाने की अनुमति देगा:

public class NinjaBuilder { 
    List<Action<Ninja>> builderActions = new List<Action<Ninja>>();

    public Ninja Build() {
        var ninja = new Ninja();
        builderActions.ForEach(ba => ba(ninja));
        return ninja;
    }

    public NinjaBuilder WithShurikens(int numShirukens) {
        builderActions.Add(n=>n.Shirukens = numShirukens);
        return this;
    }

    public NinjaBuilder When(Boolean condition) {
        if (!condition) // If the condition is not met, remove the last action
            builderActions.Remove(builderActions.Length - 1);
        return this;
    }
}

बेशक, यह मानता है कि बिल्डर निर्माण के समय स्थिति स्थिर है। यदि आप इसे निरंतर बनाना चाहते हैं, तो आप इसके बजाय ऐसा कुछ कर सकते हैं:

    public NinjaBuilder When(Func<Boolean> condition) {
        var oldAction = builderActions[builderActions.Length - 1];
        builderActions[builderActions.Length - 1] = n => condition() ? oldAction(n) : n;
        return this;
    }

यदि आप चाहते हैं कि कुछ और कंपाइलर चेक किया जाए, तो आप बिल्डर क्रियाएं सुरक्षित कर सकते हैं और ऐसा कुछ कर सकते हैं:

public class ConditionalNinjaBuilder : NinjaBuilder {
    public ConditionalNinjaBuilder(NinjaBuilder wrappedBuilder) {            
        // Since someone might call .WithShirukens on the wrapping
        // builder directly, we should make sure that our actions 
        // list is the same instance as the one in our wrapped builder
        builderActions = wrappedBuilder.builderActions;
    }

    public ConditionalNinjaBuilder When(Func<Boolean> condition) {
        var oldAction = builderActions[builderActions.Length - 1];
        builderActions[builderActions.Length - 1] = n => condition() ? oldAction(n) : n;
        return this;
    }
}

और मूल संचालन एक ConditionalNinjaBuilder वापस आते हैं:

    public ConditionalNinjaBuilder WithShurikens(int numShirukens) {
        builderActions.Add(n=>n.Shirukens = numShirukens);
        return new ConditionalNinjaBuilder(this);
    }

इस तरह आप केवल कॉल कर सकते हैं .When फिर पहली विधि को कॉल करने के बाद। इसमें संभावित रूप से नेस्टेड / मिश्रित सशर्तों की अनुमति देने का अतिरिक्त लाभ / जटिलता भी है। ओह।







method-chaining