ইনসেপশন বনাম ইনজেকশন: একটি কাঠামোর আর্কিটেকচারের সিদ্ধান্ত


28

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

আমি নিশ্চিত নই যে নির্ভরতা ইনজেকশন ব্যবহার করা এবং প্রতিটি পরিষেবায় এই সমস্ত উপাদানগুলি পরিচয় করিয়ে দেওয়া ভাল (উদাহরণস্বরূপ বৈশিষ্ট্য হিসাবে) বা আমার পরিষেবাগুলির প্রতিটি পদ্ধতির উপর আমার কোনও ধরণের মেটা ডেটা রাখা উচিত এবং এই সাধারণ কাজগুলি করতে বাধা ব্যবহার করা উচিত ?

উভয়ের উদাহরণ এখানে:

ইনজেকশন:

public class MyService
{
    public ILoggingService Logger { get; set; }

    public IEventBroker EventBroker { get; set; }

    public ICacheService Cache { get; set; }

    public void DoSomething()
    {
        Logger.Log(myMessage);
        EventBroker.Publish<EventType>();
        Cache.Add(myObject);
    }
}

এবং অন্য সংস্করণটি এখানে:

আটক:

public class MyService
{
    [Log("My message")]
    [PublishEvent(typeof(EventType))]
    public void DoSomething()
    {

    }
}

আমার প্রশ্নগুলি এখানে:

  1. জটিল কাঠামোর জন্য কোন সমাধানটি সবচেয়ে ভাল?
  2. যদি বাধা জিততে পারে তবে কোনও পদ্ধতির অভ্যন্তরীণ মানগুলির সাথে ইন্টারঅ্যাক্ট করার জন্য আমার বিকল্পগুলি কী কী (উদাহরণস্বরূপ ক্যাশে পরিষেবার সাথে ব্যবহার করতে?) উদাহরণস্বরূপ? আমি কি এই আচরণটি প্রয়োগের জন্য গুণাবলী বাদ দিয়ে অন্য উপায় ব্যবহার করতে পারি?
  3. অথবা সমস্যা সমাধানের অন্যান্য সমাধান হতে পারে?

2
আমার 1 এবং 2 তে কোনও মতামত নেই, তবে 3 সম্পর্কিত: এওপি (বিষয়টিকে কেন্দ্রিক প্রোগ্রামিং ) এবং বিশেষত স্প্রিং ডট নেট- তে সন্ধান করার বিষয়টি বিবেচনা করুন

কেবল পরিষ্কার করতে: আপনি নির্ভরতা ইনজেকশন এবং অ্যাস্পেক্ট ওরিয়েন্টেড প্রোগ্রামিংয়ের মধ্যে একটি তুলনা খুঁজছেন, সঠিক?
এমব্যাবকক

@ এমব্যাবক এইভাবে নিজে দেখেনি তবে এটি সঠিক

উত্তর:


38

লগিং, ক্যাশিং ইত্যাদির মতো ক্রস-কাটিং কনসার্নগুলি নির্ভরতা নয়, তাই পরিষেবাগুলিতে ইনজেকশন করা উচিত নয়। তবে, বেশিরভাগ লোকেরা তখন একটি সম্পূর্ণ ইন্টারলিভিং এওপি কাঠামোয় পৌঁছেছে বলে মনে হচ্ছে, এর জন্য একটি দুর্দান্ত নকশার প্যাটার্ন রয়েছে: সজ্জাকারী

উপরের উদাহরণে, মাই সার্ভিসেসটি আইএমওয়াই সার্ভিস ইন্টারফেসটি প্রয়োগ করুন:

public interface IMyService
{
    void DoSomething();
}

public class MyService : IMyService
{
    public void DoSomething()
    {
        // Implementation goes here...
    }
}

এটি মাই সার্ভিস ক্লাসকে ক্রস-কাটিং কনসার্নগুলি থেকে সম্পূর্ণ মুক্ত রাখে, এভাবে একক দায়িত্বের নীতি (এসআরপি) অনুসরণ করে।

লগিং প্রয়োগ করতে, আপনি লগিং ডেকরেটার যুক্ত করতে পারেন:

public class MyLogger : IMyService
{
    private readonly IMyService myService;
    private readonly ILoggingService logger;

    public MyLogger(IMyService myService, ILoggingService logger)
    {
        this.myService = myService;
        this.logger = logger;
    }

    public void DoSomething()
    {
        this.myService.DoSomething();
        this.logger.Log("something");
    }
}

আপনি একই পদ্ধতিতে ক্যাচিং, মিটারিং, ইভেন্টিং ইত্যাদি প্রয়োগ করতে পারেন। প্রতিটি ডেকোরেটার ঠিক একটি কাজ করে, তাই তারা এসআরপি অনুসরণ করে এবং আপনি এগুলি নির্বিচারে জটিল উপায়ে রচনা করতে পারেন। যেমন

var service = new MyLogger(
    new LoggingService(),
    new CachingService(
        new Cache(),
        new MyService());

5
ডেকোরেটর প্যাটার্নটি এই উদ্বেগগুলিকে আলাদা রাখার একটি দুর্দান্ত উপায়, তবে আপনার যদি প্রচুর পরিষেবা থাকে তবে আমি পোস্টশার্প বা ক্যাসল-ডায়নামিকপ্রক্সির মতো এওপি সরঞ্জামটি ব্যবহার করব, অন্যথায় প্রতিটি পরিষেবা শ্রেণীর ইন্টারফেসের জন্য, আমাকে ক্লাস কোড করতে হবে এবং একটি লগার সাজসজ্জা, এবং এই আলংকারীর প্রত্যেকটি সম্ভবত খুব অনুরূপ বয়লারপ্লেট কোড হতে পারে (যেমন আপনি উন্নত মডুলারাইজেশন / এনক্যাপসুলেশন পান তবে আপনি এখনও নিজেকে অনেক বার পুনরাবৃত্তি করছেন)।
ম্যাথু গ্রোভস

4
একমত। আমি গত বছর একটি বক্তৃতা দিয়েছিলাম যা কীভাবে ডেকোরেটরগুলি থেকে এওপিতে
মার্ক


নির্ভরতা ইনজেকশন সহ আমরা কীভাবে পরিষেবা এবং সজ্জকারকে ইনজেক্ট করতে পারি?
TIKSN

@ আইটি কেএসএন সংক্ষিপ্ত উত্তর: উপরে দেখানো হয়েছে । যেহেতু আপনি জিজ্ঞাসা করছেন, আপনি অবশ্যই অন্য কোনও কিছুর উত্তর খুঁজছেন, তবে আমি এটি অনুমান করতে পারি না। আপনি কি এখানে বিশদ বর্ণনা করতে পারেন, বা সম্ভবত এখানে একটি নতুন প্রশ্ন জিজ্ঞাসা করতে পারেন?
মার্ক সিমেন

6

মুষ্টিমেয় পরিষেবাগুলির জন্য, আমি মনে করি মার্কের উত্তরটি ভাল: আপনাকে কোনও নতুন 3 য় পক্ষের নির্ভরতা শিখতে বা প্রবর্তন করতে হবে না এবং আপনি এখনও ভাল সলিড নীতি অনুসরণ করবেন।

বিপুল পরিমাণ পরিষেবাদির জন্য, আমি পোস্টশার্প বা ক্যাসেল ডায়নামিকপ্রক্সির মতো একটি এওপি সরঞ্জামের সুপারিশ করব। পোস্টশার্পের একটি ফ্রি (বিয়ারের মতো) সংস্করণ রয়েছে এবং তারা সম্প্রতি ডায়াগনস্টিকসের জন্য পোস্টশার্প টুলকিট প্রকাশ করেছে (বিয়ার এবং স্পিচের মতো মুক্ত) যা আপনাকে বাক্সের বাইরে কিছু লগিং বৈশিষ্ট্য দেবে।


2

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


1

আমি বহুবার এই সমস্যার মুখোমুখি হয়েছি এবং আমি মনে করি যে আমি একটি সহজ সমাধান নিয়ে এসেছি।

প্রথমদিকে আমি ডেকরেটার প্যাটার্ন নিয়ে গিয়েছিলাম এবং ম্যানুয়ালি প্রতিটি পদ্ধতি প্রয়োগ করেছিলাম, যখন আপনার শত পদ্ধতি রয়েছে তখন এটি খুব ক্লান্তিকর হয়ে ওঠে।

তারপরে আমি পোস্টশার্প ব্যবহার করার সিদ্ধান্ত নিয়েছি তবে একটি সাধারণ লাইব্রেরি কেবলমাত্র এমন কিছু করতে যা আমি (প্রচুর) সহজ কোড দিয়ে অর্জন করতে পারি তার ধারণা পছন্দ করি না।

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

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

কোডটি এখানে:

        var linesToUse = code.Split(Environment.NewLine.ToCharArray()).Where(l => !string.IsNullOrWhiteSpace(l));
        string classLine = linesToUse.First();

        // Remove the first line this is just the class declaration, also remove its closing brace
        linesToUse = linesToUse.Skip(1).Take(linesToUse.Count() - 2);
        code = string.Join(Environment.NewLine, linesToUse).Trim()
            .TrimStart("{".ToCharArray()); // Depending on the formatting this may be left over from removing the class

        code = Regex.Replace(
            code,
            @"public\s+?(?'Type'[\w<>]+?)\s(?'Name'\w+?)\s*\((?'Args'[^\)]*?)\)\s*?\{\s*?(throw new NotImplementedException\(\);)",
            new MatchEvaluator(
                match =>
                    {
                        string start = string.Format(
                            "public {0} {1}({2})\r\n{{",
                            match.Groups["Type"].Value,
                            match.Groups["Name"].Value,
                            match.Groups["Args"].Value);

                        var args =
                            match.Groups["Args"].Value.Split(",".ToCharArray())
                                .Select(s => s.Trim().Split(" ".ToCharArray()))
                                .ToDictionary(s => s.Last(), s => s.First());

                        string call = "_decorated." + match.Groups["Name"].Value + "(" + string.Join(",", args.Keys) + ");";
                        if (match.Groups["Type"].Value != "void")
                        {
                            call = "return " + call;
                        }

                        string argsStr = args.Keys.Any(s => s.Length > 0) ? ("," + string.Join(",", args.Keys)) : string.Empty;
                        string loggedCall = string.Format(
                            "using (BuildLogger(\"{0}\"{1})){{\r\n{2}\r\n}}",
                            match.Groups["Name"].Value,
                            argsStr,
                            call);
                        return start + "\r\n" + loggedCall;
                    }));
        code = classLine.Trim().TrimEnd("{".ToCharArray()) + "\n{\n" + code + "\n}\n";

এখানে একটি উদাহরণ:

public interface ITestAdapter : IDisposable
{
    string TestMethod1();

    IEnumerable<string> TestMethod2(int a);

    void TestMethod3(List<string[]>  a, Object b);
}

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

public class LoggingTestAdapter : ITestAdapter
{

    public void Dispose()
    {
        using (BuildLogger("Dispose"))
        {
            _decorated.Dispose();
        }
    }
    public string TestMethod1()
    {
        using (BuildLogger("TestMethod1"))
        {
            return _decorated.TestMethod1();
        }
    }
    public IEnumerable<string> TestMethod2(int a)
    {
        using (BuildLogger("TestMethod2", a))
        {
            return _decorated.TestMethod2(a);
        }
    }
    public void TestMethod3(List<string[]> a, object b)
    {
        using (BuildLogger("TestMethod3", a, b))
        {
            _decorated.TestMethod3(a, b);
        }
    }
}

এটি সমর্থনকারী কোড সহ:

public class DebugLogger : ILogger
{
    private Stopwatch _stopwatch;
    public DebugLogger()
    {
        _stopwatch = new Stopwatch();
        _stopwatch.Start();
    }
    public void Dispose()
    {
        _stopwatch.Stop();
        string argsStr = string.Empty;
        if (Args.FirstOrDefault() != null)
        {
            argsStr = string.Join(",",Args.Select(a => (a ?? (object)"null").ToString()));
        }

        System.Diagnostics.Debug.WriteLine(string.Format("{0}({1}) @ {2}ms", Name, argsStr, _stopwatch.ElapsedMilliseconds));
    }

    public string Name { get; set; }

    public object[] Args { get; set; }
}

public interface ILogger : IDisposable
{
    string Name { get; set; }
    object[] Args { get; set; }
}


public class LoggingTestAdapter<TLogger> : ITestAdapter where TLogger : ILogger,new()
{
    private readonly ITestAdapter _decorated;

    public LoggingTestAdapter(ITestAdapter toDecorate)
    {
        _decorated = toDecorate;
    }

    private ILogger BuildLogger(string name, params object[] args)
    {
        return new TLogger { Name = name, Args = args };
    }

    public void Dispose()
    {
        _decorated.Dispose();
    }

    public string TestMethod1()
    {
        using (BuildLogger("TestMethod1"))
        {
            return _decorated.TestMethod1();
        }
    }
    public IEnumerable<string> TestMethod2(int a)
    {
        using (BuildLogger("TestMethod2", a))
        {
            return _decorated.TestMethod2(a);
        }
    }
    public void TestMethod3(List<string[]> a, object b)
    {
        using (BuildLogger("TestMethod3", a, b))
        {
            _decorated.TestMethod3(a, b);
        }
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.