NInject ব্যবহার করে কারখানা তৈরির সর্বোত্তম উপায় কী?


27

আমি এমভিসি 3-তে এনআইজেক্ট ব্যবহার করে নির্ভরতা ইনজেকশন নিয়ে বেশ স্বাচ্ছন্দ্য বোধ করি। একটি এমভিসি 3 অ্যাপ্লিকেশনে কাজ করার সময়, আমি এনআইএনজেক্ট ব্যবহার করে একটি কাস্টম কন্ট্রোলার ক্রিয়েশন ফ্যাক্টরি তৈরি করেছি, সুতরাং যে কোনও কন্ট্রোলার তৈরি হয় তা এই কন্ট্রোলার কারখানার মাধ্যমে নির্ভরতা injুকিয়ে দেয়।

এখন আমি একটি উইন্ডোজ অ্যাপ্লিকেশন বিকাশ করা শুরু করছি, আমি অ্যাপ্লিকেশন প্রশস্ত নির্ভরতা ইনজেকশন ব্যবহার করতে চাই। অর্থাত্ প্রতিটি বস্তুটি অবশ্যই এনআইজেক্টের মাধ্যমে তৈরি করা উচিত, যাতে ইউনিট পরীক্ষাটি সহজ করা যায়। দয়া করে আমাকে গাইড করে নিশ্চিত করুন যে তৈরি করা প্রতিটি বস্তু অবশ্যই কেবল এনআইনজেক্ট ফ্যাক্টরির হওয়া উচিত।

উদাহরণস্বরূপ, যদি Button_Clickআমি কোনও উইন্ডোতে ইভেন্টে ফর্ম লিখি তবে:

TestClass testClass = new TestClass()

এবং TestClassএর উপর কোনও নির্ভরতা আছে, বলুন, ITestতবে এটি স্বয়ংক্রিয়ভাবে সমাধান করা উচিত। আমি জানি আমি ব্যবহার করতে পারি:

Ikernel kernel = new StandardKenel()
//AddBinding()
TestClass testClass = kenel.get<TestClass>();

তবে আমি যখনই কোনও অবজেক্ট তৈরি করতে চাইছি তখন এটি করা ক্লান্তিকর বলে মনে করি। এটি বিকাশকারীকে একটি নির্দিষ্ট উপায়ে অবজেক্টটি তৈরি করতে বাধ্য করে। এটি কি আরও ভাল করা যায়?

আমার কী অবজেক্ট তৈরির জন্য কেন্দ্রীয় ভাণ্ডার থাকতে পারে এবং তারপরে প্রতিটি বস্তু তৈরির স্বয়ংক্রিয়ভাবে সেই সংগ্রহস্থান ব্যবহার করা যাবে?


1
হাই প্রবিন পাতিল: দুর্দান্ত প্রশ্ন। আপনি যা জিজ্ঞাসা করছেন সে সম্পর্কে এটি পরিষ্কার করার জন্য আমি আপনার শিরোনামে একটি সামান্য পরিবর্তন করেছি; আমি চিহ্নটি মিস করলে সংশোধন করতে দ্বিধা বোধ করি।

@ মার্কট্র্যাপ: উপযুক্ত শিরোনামের জন্য ধন্যবাদ আমি সেই ট্যাগলাইনটি মিস করেছি ...
প্রভিন পাতিল

গৌণ পার্শ্ব নোট হিসাবে, প্রকল্পটি "এনজেক্ট" নয়, "নিনজেক্ট" বানানযুক্ত। যদিও এটি এন-ইনজেকশন হতে পারে, তারা আজকাল বেশ কিছুটা নিন-জা থিমে খেলেন। :) সিএফ। ninject.org
কর্নেলিয়াস

উত্তর:


12

ক্লায়েন্ট অ্যাপ্লিকেশনগুলির জন্য, প্রায়শই এমভিপি (বা এমভিভিএম) এর মতো কোনও প্যাটার্নটি মানিয়ে নেওয়া এবং ফর্ম থেকে অন্তর্নিহিত ভিউমোডেল বা উপস্থাপকের কাছে ডেটা-বাইন্ডিং ব্যবহার করা ভাল।

ভিউমোডেলসের জন্য, আপনি স্ট্যান্ডার্ড কনস্ট্রাক্টর ইঞ্জেকশন ব্যবহার করে প্রয়োজনীয় নির্ভরতাগুলি ইনজেকশন করতে পারেন।

আপনার অ্যাপ্লিকেশনটির কম্পোজিশন রুটে আপনি আপনার অ্যাপ্লিকেশনের পুরো অবজেক্টের গ্রাফটি ওয়্যার আপ করতে পারেন । এর জন্য আপনার কোনও ডিআই কনটেইনার (যেমন নিনজেক্ট) ব্যবহার করার দরকার নেই, তবে আপনি এটি করতে পারেন।


7

উইন্ডোজ ফর্ম অ্যাপ্লিকেশনগুলির সাধারণত এন্ট্রি করার একটি বিন্দু থাকে যা দেখতে এই জাতীয় দেখাচ্ছে:

    // Program.cs
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }

আপনি যদি এই সংকেতটিকে আপনার কম্পোজিশনের মূলটি কোড করে থাকেন তবে আপনি যেখানে কোড পেয়েছেন সেখানে স্পষ্টভাবে নিিনজেক্টকে অনুরোধ করছেন এমন জায়গা সংখ্যাটি বিস্তৃতভাবে হ্রাস করতে পারবেন যেন এটি কোনও সার্ভিস লোকেটার।

    // Program.cs
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        var kernel = InitializeNinjectKernel();
        Application.Run(kernel.Get<MainForm>());
    }

এই বিন্দু থেকে, আপনি কনস্ট্রাক্টর ইনজেকশনের মাধ্যমে আপনার সমস্ত নির্ভরতা ইনজেক্ট করুন।

public MainForm(TestClass testClass) {
    _testClass = testClass;
}

যদি আপনার "নির্ভরতা" এমন এক জিনিস হয় যা আপনি একাধিকবার উত্পাদন করতে সক্ষম হন তবে আপনার যা প্রয়োজন তা কারখানা:

public MainForm(IFactory<TestClass> testClassFactory) {
    _testClassFactory = testClassFactory;
}

...
var testClass = _testClassFactory.Get();

এক টন এক অফ অফ বাস্তবায়ন এড়াতে আপনি এইভাবে আইফ্যাক্টরি ইন্টারফেসটি প্রয়োগ করতে পারেন:

public class InjectionFactory<T> : IFactory<T>, IObjectFactory<T>, IDependencyInjector<T>
{
    private readonly IKernel _kernel;
    private readonly IParameter[] _contextParameters;

    public InjectionFactory(IContext injectionContext)
    {
        _contextParameters = injectionContext.Parameters
            .Where(p => p.ShouldInherit).ToArray();
        _kernel = injectionContext.Kernel;
    }

    public T Get()
    {
        try
        {
            return _kernel.Get<T>(_contextParameters.ToArray());
        }
        catch (Exception e)
        {
            throw new Exception(
                string.Format("An error occurred while attempting to instantiate an object of type <{0}>",
                typeof(T)));
        }
    }

...
Bind(typeof (IFactory<>)).To(typeof (InjectionFactory<>));
Bind(typeof (IContext)).ToMethod(c => c.Request.ParentContext);

দয়া করে, আপনার কি এই কারখানার সম্পূর্ণ বাস্তবায়ন রয়েছে?
টেবো

@ কলরব্লেন্ড: না, তবে আমি InjectionFactory<T>প্রয়োগ করা অন্যান্য ইন্টারফেসগুলি থেকে যদি আপনি মুক্তি পান তবে প্রদত্ত কোডটি ঠিক কাজ করা উচিত। বিশেষত এমন কিছু আছে যা নিয়ে আপনার সমস্যা হচ্ছে?
স্ট্রিপলিং ওয়ারিয়র

আমি এটি ইতিমধ্যে বাস্তবায়ন করেছি, আমি কেবল জানতে চাই ক্লাসের মধ্যে অন্যান্য আকর্ষণীয় জিনিস ছিল কিনা।
টেবো

@ টিবো: আমি কেবলমাত্র এটির সাথে ডিআই-সম্পর্কিত অন্যান্য কয়েকটি ইন্টারফেস প্রয়োগ করেছি, যেমন আপনি একটি কারখানাকে পাশ করতে পেরেছিলেন Type, তবে এটি গ্যারান্টি দেয় যে এটির জন্য হাইড্রেট করা বস্তুগুলি Typeপ্রদত্ত জেনেরিক ধরণের প্রয়োগ বা প্রসারিত করে। খুব বিশেষ কিছু না।
স্ট্রিপলিং ওয়ারিয়র

4

আমি সবসময় যে কোনও আইওসি কনটেইনারটির জন্য অ্যাডাপ্টারের মোড়ক লিখি, যা দেখতে এটির মতো দেখাচ্ছে:

public static class Ioc
{
    public static IIocContainer Container { get; set; }
}

public interface IIocContainer 
{
    object Get(Type type);
    T Get<T>();
    T Get<T>(string name, string value);
    void Inject(object item);
    T TryGet<T>();
}

নিিনজেক্টের জন্য, বিশেষত, কংক্রিট অ্যাডাপ্টার শ্রেণিটি দেখতে এরকম দেখাচ্ছে:

public class NinjectIocContainer : IIocContainer
{
    public readonly IKernel Kernel;
    public NinjectIocContainer(params INinjectModule[] modules) 
    {
        Kernel = new StandardKernel(modules);
        new AutoWirePropertyHeuristic(Kernel);
    }

    private NinjectIocContainer()
    {
        Kernel = new StandardKernel();
        Kernel.Load(AppDomain.CurrentDomain.GetAssemblies());

        new AutoWirePropertyHeuristic(Kernel);
    }

    public object Get(Type type)
    {
        try
        {
            return Kernel.Get(type);
        }
        catch (ActivationException exception)
        {
            throw new TypeNotResolvedException(exception);
        }              
    }

    public T TryGet<T>()
    {
        return Kernel.TryGet<T>();
    }

    public T Get<T>()
    {
        try
        {
            return Kernel.Get<T>();
        }
        catch (ActivationException exception)
        {
            throw new TypeNotResolvedException(exception);
        }           
    }

    public T Get<T>(string name, string value)
    {
        var result = Kernel.TryGet<T>(metadata => metadata.Has(name) &&
                     (string.Equals(metadata.Get<string>(name), value,
                                    StringComparison.InvariantCultureIgnoreCase)));

        if (Equals(result, default(T))) throw new TypeNotResolvedException(null);
            return result;
    }

    public void Inject(object item)
    {
        Kernel.Inject(item);
    }
}

এটি করার প্রাথমিক কারণটি হ'ল আইওসি কাঠামোটি বিমূর্ত করা, সুতরাং আমি যে কোনও সময় এটি প্রতিস্থাপন করতে পারি - প্রদত্ত যে ফ্রেমওয়ার্কগুলির মধ্যে পার্থক্যটি সাধারণত ব্যবহারের চেয়ে কনফিগারেশনে থাকে।

তবে, বোনাস হিসাবে, অন্যান্য ফ্রেমওয়ার্কগুলির মধ্যে আইওসি ফ্রেমওয়ার্ক ব্যবহার করার জন্য জিনিসগুলি অনেক সহজ হয়ে যায় যা এটিকে সহজাতভাবে সমর্থন করে না। উইনফরমের জন্য, উদাহরণস্বরূপ, এটি দুটি ধাপ:

আপনার মূল পদ্ধতিতে, অন্য কিছু করার আগে কেবল একটি ধারক ইনস্ট্যান্ট করুন।

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        try
        {
            Ioc.Container = new NinjectIocContainer( /* include modules here */ );
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyStartupForm());
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
        }
    }
}

এবং তারপরে একটি বেস ফর্ম রয়েছে, যা থেকে অন্যান্য ফর্মগুলি আসে, যা নিজেই ইনজেকশনকে কল করে।

public IocForm : Form
{
    public IocForm() : base()
    {
        Ioc.Container.Inject(this);
    }
}

এটি অটো-ওয়্যারিং হিউরিস্টিককে বলে যে ফর্মের সমস্ত বৈশিষ্ট্যগুলি পুনরাবৃত্তভাবে ইনজেক্ট করার চেষ্টা করতে যা আপনার মডিউলগুলিতে সেট করা নিয়মগুলির সাথে খাপ খায়।


খুব সুন্দর সমাধান ..... আমি একবার চেষ্টা করে দেখব।
প্রবিন পাতিল

10
এটি সার্ভিস লোকেটার, যা দর্শনীয়ভাবে খারাপ ধারণা: Blog.ploeh.dk/2010/02/03/SCLLocatorIsAnAntiPattern.aspx
মার্ক

2
@ মার্কসিম্যান: একটি সার্ভিস লোকেটার একটি খারাপ ধারণা, যদি আপনি সর্বত্র থেকে এটি অ্যাক্সেস করেন তবে এটি আপনার উপরের স্তরের অবজেক্টগুলিকে যতটা সম্ভব তারের উপর চাপ দেয় না। মার্কের নিজস্ব মন্তব্য পড়ুন, পৃষ্ঠাটি থেকে কিছুটা দূরে: "এই জাতীয় ক্ষেত্রে আপনার প্রতিটি পদার্থে (যেমন পৃষ্ঠা) রচনার রুটটি সরানো ছাড়া সত্যই কোন উপায় নেই এবং আপনার ডিআই কনটেইনারকে সেখান থেকে আপনার নির্ভরতা বাড়িয়ে দিন This সার্ভিস লোকেটারটি অ্যান্টি-প্যাটার্ন, তবে এটি এখনও নয় যে আপনি এখনও ধারক ব্যবহারকে সর্বনিম্ন ন্যূনতম রাখেন keep " (সম্পাদনা করুন: অপেক্ষা করুন, আপনিই মার্ক! তো পার্থক্য কী?)
পিডিআর

1
পার্থক্যটি হ'ল আপনি কোনও বাক্সে কোনও একক সার্ভিস লোকেটার উপলব্ধ করার পরিবর্তে আপনার বাকী কোড বেস রচয়িতা থেকে রক্ষা করতে পারেন।
মার্ক সিমেন

2
@ পিডিআর: আমার অভিজ্ঞতা অনুসারে, আপনি যদি অ্যাট্রিবিউট ক্লাসের মতো জিনিসগুলিতে পরিষেবাগুলি ইনজেক্ট করার চেষ্টা করছেন, তবে আপনি উদ্বেগগুলি সঠিকভাবে আলাদা করছেন না। এমন কিছু ক্ষেত্রে রয়েছে যেখানে আপনি যে কাঠামোটি ব্যবহার করছেন তা যথাযথভাবে নির্ভরতা ইনজেকশনটি ব্যবহার করা অসম্ভব করে তোলে এবং কখনও কখনও আমরা কোনও সার্ভিস লোকেটার ব্যবহার করতে বাধ্য হই, তবে আমি অবশ্যই এটির দিকে ফিরে যাওয়ার আগে যথাসম্ভব যথাযথ ডিআই নেওয়ার চেষ্টা করব প্যাটার্ন।
স্ট্রিপলিং ওয়ারিয়র

1

নির্ভরতা ইনজেকশন এর ভাল ব্যবহার সাধারণত কোড পৃথক করার উপর নির্ভর করে যা বস্তু এবং প্রকৃত ব্যবসায়িক যুক্তি তৈরি করে। অন্য কথায়, আমি চাইব না যে আমার দলটি ঘন ঘন ব্যবহার করে newএবং সেভাবে কোনও শ্রেণীর উদাহরণ তৈরি করে। এটি সম্পন্ন হয়ে গেলে, অন্যটির জন্য তৈরি হওয়া প্রকারটি সহজেই অদলবদল করার কোনও উপায় নেই, যেহেতু আপনি ইতিমধ্যে কংক্রিটের ধরণটি নির্দিষ্ট করেছেন।

সুতরাং এটি ঠিক করার দুটি উপায় আছে:

  1. ক্লাসের যে উদাহরণগুলির প্রয়োজন হবে সেগুলি ইনজেক্ট করুন। আপনার উদাহরণে, TestClassআপনার উইন্ডোজ ফর্মটিতে একটি ইনজেক্ট করুন যাতে এটির প্রয়োজন হওয়ার আগেই এটির উদাহরণ থাকে। যখন নিনেক্ট আপনার ফর্মটি ইনস্ট্যান্ট করে, এটি স্বয়ংক্রিয়ভাবে নির্ভরতা তৈরি করে।
  2. ক্ষেত্রে যেখানে আপনি কি সত্যিই ইন না একটি দৃষ্টান্ত পর্যন্ত আপনি এটি প্রয়োজন তৈরি করতে চান, আপনি বিজনেস লজিক মধ্যে একটি কারখানা উদ্বুদ্ধ করতে পারেন। উদাহরণস্বরূপ, আপনি IKernelআপনার উইন্ডোজ ফর্মটিতে একটি ইনজেক্ট করতে পারেন এবং তারপরে তা ইনস্ট্যান্টিয়েট করতে পারেন TestClass। আপনার স্টাইলের উপর নির্ভর করে এটি সম্পাদন করার অন্যান্য উপায়ও রয়েছে (কারখানার শ্রেণি, কারখানার প্রতিনিধি ইত্যাদি) ing

এটি করার ফলে টেস্টক্লাসের কংক্রিট ধরণের উভয়কেই সরিয়ে আনা সহজ হয়, পাশাপাশি পরীক্ষার শ্রেণিটি ব্যবহার করে এমন কোডটি সংশোধন না করেই পরীক্ষার শ্রেণির প্রকৃত নির্মাণকে সংশোধন করে ।


1

আমি Ninject ব্যবহার করা করেছি, কিন্তু কাপড় তৈরি যখন আপনি একটি আইওসি ব্যবহার করছেন আদর্শ উপায় একটি মাধ্যমে এটি করতে হয় Func<T>যেখানে Tবস্তুর ধরনের তৈরি করতে চান হয়। সুতরাং যদি অবজেক্টটি যদি T1টাইপের অবজেক্ট তৈরি করতে হয় T2তবে কনস্ট্রাক্টরের T1অবশ্যই টাইপের পরামিতি থাকতে হবে Func<T1>যা ক্ষেত্র / সম্পত্তি হিসাবে সংরক্ষণ করা হয় T2। এখন আপনি ধরনের বস্তু তৈরি করতে চান যখন T2মধ্যে T1ডাকা আপনি Func

এটি আপনাকে আপনার আইওসি কাঠামো থেকে পুরোপুরি ডেস্পল করে এবং আইওসি মানসিকতায় কোড করার সঠিক উপায়।

এটি Funcকরার খারাপ দিকটি হ'ল এটি বিরক্তিকর হয়ে উঠতে পারে যখন আপনাকে ম্যানুয়ালি তারের দরকার হয় বা উদাহরণস্বরূপ যখন আপনার স্রষ্টাকে কিছু প্যারামিটারের প্রয়োজন হয়, তাই আইওসি আপনার পক্ষে স্বতঃসংশ্লিষ্ট হতে পারে না Func

http://code.google.com/p/autofac/wiki/RelationshipTypes

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.