আমি কী এনক্যাপসুলেশন না ভেঙে Dependency Injection ব্যবহার করতে পারি?


15

এখানে আমার সমাধান এবং প্রকল্পগুলি:

  • বইয়ের দোকান (সমাধান)
    • BookStore.Coupler (প্রকল্প)
      • Bootstrapper.cs
    • BookStore.Domain (প্রকল্প)
      • CreateBookCommandValidator.cs
      • CompositeValidator.cs
      • IValidate.cs
      • IValidator.cs
      • ICommandHandler.cs
    • বুকস্টোর.আইনফ্রাস্ট্রাকচার (প্রকল্প)
      • CreateBookCommandHandler.cs
      • ValidationCommandHandlerDecorator.cs
    • BookStore.Web (প্রকল্প)
      • Global.asax
    • BookStore.BatchProcesses (প্রকল্প)
      • Program.cs

Bootstrapper.cs :

public static class Bootstrapper.cs 
{
    // I'm using SimpleInjector as my DI Container
    public static void Initialize(Container container) 
    {
        container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>), typeof(CreateBookCommandHandler).Assembly);
        container.RegisterDecorator(typeof(ICommandHandler<>), typeof(ValidationCommandHandlerDecorator<>));
        container.RegisterManyForOpenGeneric(typeof(IValidate<>),
            AccessibilityOption.PublicTypesOnly,
            (serviceType, implTypes) => container.RegisterAll(serviceType, implTypes),
            typeof(IValidate<>).Assembly);
        container.RegisterSingleOpenGeneric(typeof(IValidator<>), typeof(CompositeValidator<>));
    }
}

CreateBookCommandValidator.cs

public class CreateBookCommandValidator : IValidate<CreateBookCommand>
{
    public IEnumerable<IValidationResult> Validate(CreateBookCommand book)
    {
        if (book.Author == "Evan")
        {
            yield return new ValidationResult<CreateBookCommand>("Evan cannot be the Author!", p => p.Author);
        }
        if (book.Price < 0)
        {
            yield return new ValidationResult<CreateBookCommand>("The price can not be less than zero", p => p.Price);
        }
    }
}

CompositeValidator.cs

public class CompositeValidator<T> : IValidator<T>
{
    private readonly IEnumerable<IValidate<T>> validators;

    public CompositeValidator(IEnumerable<IValidate<T>> validators)
    {
        this.validators = validators;
    }

    public IEnumerable<IValidationResult> Validate(T instance)
    {
        var allResults = new List<IValidationResult>();

        foreach (var validator in this.validators)
        {
            var results = validator.Validate(instance);
            allResults.AddRange(results);
        }
        return allResults;
    }
}

IValidate.cs

public interface IValidate<T>
{
    IEnumerable<IValidationResult> Validate(T instance);
}

IValidator.cs

public interface IValidator<T>
{
    IEnumerable<IValidationResult> Validate(T instance);
}

ICommandHandler.cs

public interface ICommandHandler<TCommand>
{
    void Handle(TCommand command);
}

CreateBookCommandHandler.cs

public class CreateBookCommandHandler : ICommandHandler<CreateBookCommand>
{
    private readonly IBookStore _bookStore;

    public CreateBookCommandHandler(IBookStore bookStore)
    {
        _bookStore = bookStore;
    }

    public void Handle(CreateBookCommand command)
    {
        var book = new Book { Author = command.Author, Name = command.Name, Price = command.Price };
        _bookStore.SaveBook(book);
    }
}

ValidationCommandHandlerDecorator.cs

public class ValidationCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decorated;
    private readonly IValidator<TCommand> validator;

    public ValidationCommandHandlerDecorator(ICommandHandler<TCommand> decorated, IValidator<TCommand> validator)
    {
        this.decorated = decorated;
        this.validator = validator;
    }

    public void Handle(TCommand command)
    {
        var results = validator.Validate(command);

        if (!results.IsValid())
        {
            throw new ValidationException(results);
        }

        decorated.Handle(command);
    }
}

Global.asax

// inside App_Start()
var container = new Container();
Bootstrapper.Initialize(container);
// more MVC specific bootstrapping to the container. Like wiring up controllers, filters, etc..

Program.cs

// Pretty much the same as the Global.asax

সমস্যার দীর্ঘতর সেটআপের জন্য দুঃখিত, আমার আসল সমস্যাটি বর্ণনা করার চেয়ে আমার কাছে এটিকে ব্যাখ্যা করার আর ভাল উপায় নেই।

আমি আমার ক্রিয়েটবুককম্যান্ডভালিডেটর বানাতে চাই না public । আমি বরং এটি internalচাই internalতবে আমি যদি এটি তৈরি করি তবে আমি এটি আমার ডিআই কনটেইনারের সাথে নিবন্ধ করতে সক্ষম হব না। আমি এটি অভ্যন্তরীণ হতে চাই কারণ হ'ল একমাত্র প্রকল্প যা আমার আইভিলেটিট <> বাস্তবায়নের ধারণা থাকা উচিত বুকস্টোর.ডমেন প্রকল্পে। অন্য যে কোনও প্রকল্পে কেবল আইভিলেটেটর <> গ্রহণ করা প্রয়োজন এবং কমপোজাইটভালিডেটর সমাধান করা উচিত যা সমস্ত বৈধতা পূরণ করবে।

এনক্যাপসুলেশন না ভেঙে আমি কীভাবে নির্ভরতা ইনজেকশন ব্যবহার করতে পারি? নাকি আমি এই সব ভুল করছি?


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

1
@ ইউফোরিক: এটি সঠিক। এটি কমান্ড প্যাটার্ন নয় । প্রকৃতপক্ষে, ওপি একটি পৃথক ধরণ অনুসরণ করে: কমান্ড / হ্যান্ডলার প্যাটার্ন
স্টিভেন

অনেক ভাল উত্তর ছিল আমি আশা করি আমি উত্তর হিসাবে আরও চিহ্নিত করতে পারে। সাহায্যের জন্য সবাইকে ধন্যবাদ।
ইভান লারসেন

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

উত্তর:


11

তৈরি করছে CreateBookCommandValidator প্রকাশ্য এনক্যাপস্যুলেশন লঙ্ঘন করে না, যেহেতু

এনক্যাপসুলেশন কোনও শ্রেণীর অভ্যন্তরে কাঠামোগত ডেটা অবজেক্টের মান বা স্থিতি লুকিয়ে রাখতে ব্যবহার করা হয়, অননুমোদিত দলগুলিতে তাদের সরাসরি প্রবেশাধিকার রোধ করে ( উইকিপিডিয়া )

তোমার CreateBookCommandValidator তার ডেটা সদস্যদের অ্যাক্সেসের মঞ্জুরি দেয় না, যাতে তার লঙ্ঘন না এনক্যাপস্যুলেশন (এটা বর্তমানে কোনো আছে বলে মনে হচ্ছে না)।

এই শ্রেণিটিকে সর্বজনীন করে তোলা অন্য কোনও নীতি লঙ্ঘন করে না (যেমন সলাইড ID নীতিগুলি) , কারণ:

  • এই শ্রেণীর একটি একক সুস্পষ্ট দায়িত্ব রয়েছে এবং তাই একক দায়িত্বের নীতি অনুসরণ করে।
  • সিস্টেমে নতুন ভ্যালিডিটার যুক্ত করা কোডের একক লাইন পরিবর্তন না করেই করা যেতে পারে এবং সুতরাং আপনি ওপেন / ক্লোজড নীতি অনুসরণ করেন।
  • এই আইভালিডেটর <টি> ইন্টারফেস যে এই শ্রেণীর প্রয়োগগুলি সংকীর্ণ (কেবলমাত্র একটি সদস্য রয়েছে) এবং ইন্টারফেস বিভাজন নীতি অনুসরণ করে।
  • আপনার গ্রাহকরা কেবলমাত্র এই আইভিডিয়েটার <T> ইন্টারফেসের উপর নির্ভর করে এবং তাই নির্ভরতা বিপরীতমুখী নীতি অনুসরণ করে।

CreateBookCommandValidatorক্লাসটি গ্রন্থাগারের বাইরে থেকে সরাসরি গ্রাস না করা হলে আপনি কেবলমাত্র অভ্যন্তরীণ তৈরি করতে পারেন তবে এটির ক্ষেত্রে খুব কমই ঘটতে পারে, কারণ আপনার ইউনিট পরীক্ষাগুলি এই শ্রেণীর একটি গুরুত্বপূর্ণ ভোক্তা (এবং আপনার সিস্টেমের প্রায় প্রতিটি ক্লাস)।

আপনি ইউনিট পরীক্ষা প্রকল্পটিকে আপনার প্রকল্পের অভ্যন্তরীণ অ্যাক্সেসের অনুমতি দেওয়ার জন্য শ্রেণি অভ্যন্তরীণ করতে পারেন এবং [ইন্টার্নালভিসিবলটো] ব্যবহার করতে পারেন তবে কেন বিরক্ত করবেন?

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

তবে, আপনি যদি পুনরায় ব্যবহারযোগ্য প্রকল্প তৈরি না করে থাকেন তবে এই সমস্যাটির অস্তিত্ব নেই। এটি বিদ্যমান নেই, কারণ আপনি যে প্রকল্পগুলি এর উপর নির্ভরশীল তা পরিবর্তন করতে পারবেন এবং আপনার দলের অন্যান্য বিকাশকারীদের আপনার গাইডলাইন অনুসরণ করতে হবে। এবং একটি সহজ গাইডলাইন করবে: একটি বিমূর্তকরণের জন্য প্রোগ্রাম; বাস্তবায়ন নয় (নির্ভরতা বিপরীতমুখী মূলনীতি)।

এত দীর্ঘ গল্প সংক্ষিপ্ত, আপনি যদি পুনরায় ব্যবহারযোগ্য লাইব্রেরি না লিখেন তবে এই শ্রেণিটিকে অভ্যন্তরীণ করবেন না।

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

container.RegisterManyForOpenGeneric(typeof(IValidate<>),
    AccessibilityOption.AllTypes,
    container.RegisterAll,
    typeof(IValidate<>).Assembly);

নিশ্চিত করার একমাত্র জিনিস আপনার সমস্ত বৈধকারীর অভ্যন্তরীণ সত্ত্বেও জনসাধারণ নির্মাতা রয়েছে ruct আপনি যদি প্রকৃতপক্ষে আপনার ধরণের অভ্যন্তরীণ নির্মাণকারী চান (তবে কেন আপনি তা চান তা জানেন না) তবে আপনি কনস্ট্রাক্টর রেজোলিউশন আচরণটি ওভাররাইড করতে পারেন ।

হালনাগাদ

সরল ইনজেক্টর v2.6 যেহেতু , এর ডিফল্ট আচরণ RegisterManyForOpenGenericহ'ল জনসাধারণ এবং অভ্যন্তরীণ উভয় প্রকারের নিবন্ধন করা। সুতরাং সরবরাহ AccessibilityOption.AllTypesএখন অপ্রয়োজনীয় এবং নিম্নলিখিত বিবৃতিটি সর্বজনীন এবং অভ্যন্তরীণ উভয় প্রকারের নিবন্ধভুক্ত করবে:

container.RegisterManyForOpenGeneric(typeof(IValidate<>),
    container.RegisterAll,
    typeof(IValidate<>).Assembly);

8

এটা বড় কথা নয় যে CreateBookCommandValidator ক্লাসটি সর্বজনীন ।

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

অন্যথায়, আপনি যদি ক্লায়েন্টদের ক্লাস সম্পর্কে না জানার জন্য সত্যই জোর করতে চান তবে আপনি ক্লাসটি প্রকাশের পরিবর্তে পাবলিক স্ট্যাটিক কারখানা পদ্ধতিও ব্যবহার করতে পারেন, যেমন:

public static class Validators
{
    public static IValidate<CreateBookCommand> NewCreateBookCommandValidator()
    {
        return new CreateBookCommnadValidator();
    }
}

আপনার ডিআই কনটেইনারে নিবন্ধকরণের জন্য, আমি জানি যে সমস্ত ডিআই কনটেইনার স্থির কারখানার পদ্ধতি ব্যবহার করে নির্মাণের অনুমতি দেয়।


হ্যাঁ, আপনাকে ধন্যবাদ .. আমি এই পোস্টটি তৈরি করার আগে আমি একই জিনিসটি ভাবছিলাম। আমি এমন একটি ফ্যাক্টরি ক্লাস তৈরি করার কথা ভাবছিলাম যা যথাযথ আইভালিডেট <> বাস্তবায়নে ফিরে আসবে তবে আইভিডিলেট <> বাস্তবায়নের যদি কোনওর উপর নির্ভরশীলতা থাকে তবে এটি সম্ভবত বেশ লোমশ হয়ে উঠবে।
ইভান লারসেন

@ ইভানলারসেন কেন? যদি IValidate<>বাস্তবায়নের নির্ভরতা থাকে, তবে এই নির্ভরতাগুলি কারখানা পদ্ধতিতে পরামিতি হিসাবে রাখুন।
ঝমিনাল

5

এটি সমাবেশ হিসাবে দৃশ্যমান করার জন্য আপনি একটি আবেদন CreateBookCommandValidatorহিসাবে ইন্টার্নিভিজিবলটোঅ্যাট্রিবিউট হিসাবে ঘোষণা করতে পারেন । ইউনিট পরীক্ষা করার সময় এটি প্রায়শই সহায়তা করে।internalBookStore.Coupler


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

4

আপনি এটিকে অভ্যন্তরীণ করে তুলতে পারেন এবং অভ্যন্তরীণ দৃশ্যমান টোঅ্যাট্রিবিউট এমএসডিএন.লিঙ্কটি ব্যবহার করতে পারেন যাতে আপনার পরীক্ষার কাঠামো / প্রকল্প এটি অ্যাক্সেস করতে পারে।

আমার একটি সম্পর্কিত সমস্যা ছিল -> লিঙ্ক

সমস্যা সম্পর্কিত আরও একটি স্ট্যাকওভারফ্লো প্রশ্নের এখানে একটি লিঙ্ক রয়েছে :

এবং অবশেষে ওয়েবে একটি নিবন্ধ


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

1

আরেকটি বিকল্প হ'ল জনসাধারণকে প্রকাশ করা কিন্তু এটি অন্য একটি সমাবেশে রাখা।

সুতরাং মূলত, আপনার একটি সার্ভিস ইন্টারফেস সমাবেশ, একটি পরিষেবা বাস্তবায়ন সমাবেশ (যা পরিষেবা ইন্টারফেসের রেফারেন্স করে), একটি পরিষেবা গ্রাহক সমাবেশ (যা পরিষেবা ইন্টারফেসের রেফারেন্স করে) এবং একটি আইওসি রেজিস্ট্রার সমাবেশ (যা উভয় পরিষেবা ইন্টারফেস এবং পরিষেবা বাস্তবায়ন উভয়কে একত্রিত করার জন্য রেফারেন্স করে) )।

আমার স্ট্রেস করা উচিত, এটি সবসময় উপযুক্ত সমাধান নয়, তবে এটি বিবেচনার জন্য একটি।


এটি কী অভ্যন্তরীণদের দৃশ্যমান করার অসুবিধাজনিত সুরক্ষা ঝুঁকি সরিয়ে ফেলবে?
জোহানেস

1
@ জোহানেস: সুরক্ষা ঝুঁকিপূর্ণ? আপনি যদি সুরক্ষা দেওয়ার জন্য অ্যাক্সেস সংশোধকগুলির উপর নির্ভর করেন তবে আপনার চিন্তার দরকার। আপনি প্রতিবিম্বের মাধ্যমে যে কোনও পদ্ধতিতে অ্যাক্সেস পেতে পারেন। তবে এটি বাস্তবায়ন উল্লেখযোগ্য নয় এমন আরও একটি সমাবেশে প্রয়োগের মাধ্যমে অভ্যন্তরীণগুলিতে সহজে / উত্সাহিত অ্যাক্সেসকে সরিয়ে দেয়।
পিডিআর
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.