নির্ভরতা ইনজেকশন কৌশলটিতে বেশ কয়েকটি প্রধান লক্ষ্য রয়েছে, যা (তবে সীমাবদ্ধ নয়) সহ:
- আপনার সিস্টেমের অংশগুলির মধ্যে সংযুক্তি হ্রাস করা। এইভাবে আপনি কম চেষ্টা করে প্রতিটি অংশ পরিবর্তন করতে পারেন। দেখুন "হাই সংযোগ, কম কাপলিং"
- দায়িত্ব সম্পর্কে কঠোর নিয়ম প্রয়োগ করা। একটি সত্তা অবশ্যই তার বিমূর্ততার স্তরে কেবল একটি কাজ করতে হবে। অন্যান্য সত্তাগুলি অবশ্যই এটির নির্ভরতা হিসাবে সংজ্ঞায়িত করা উচিত। দেখুন "আইওসি"
- ভাল পরীক্ষার অভিজ্ঞতা। সুস্পষ্ট নির্ভরতা আপনাকে এমন কিছু আদিম পরীক্ষার আচরণের মাধ্যমে আপনার সিস্টেমের বিভিন্ন অংশকে স্টাব করতে দেয় যা আপনার প্রোডাকশন কোডের তুলনায় একই পাবলিক এপিআই রয়েছে। দেখুন "ঠাট্টা arent 'নিবন্ধসমূহ"
মনে রাখা অন্য জিনিসটি হ'ল আমরা সাধারণত বিমূর্তির উপর নির্ভর করব, বাস্তবায়ন নয়। আমি প্রচুর লোককে দেখি যারা কেবল নির্দিষ্ট প্রয়োগের জন্য ইঞ্জিন করতে ডিআই ব্যবহার করে। একটি বড় পার্থক্য আছে।
কারণ আপনি যখন ইনজেক্ট করেন এবং কোনও বাস্তবায়নের উপর নির্ভর করেন তখন আমরা কোন পদ্ধতিতে অবজেক্ট তৈরি করতে ব্যবহার করি তার মধ্যে কোনও পার্থক্য নেই। এটা ঠিক কোন ব্যাপার না। উদাহরণস্বরূপ, আপনি যদি requests
যথাযথ বিমূর্ততা ছাড়াই ইনজেকশন করেন তবে আপনার এখনও একই পদ্ধতি, স্বাক্ষর এবং রিটার্নের ধরণের সাথে অনুরূপ কিছু প্রয়োজন হবে। আপনি এই বাস্তবায়নটি মোটেও প্রতিস্থাপন করতে পারবেন না। তবে, আপনি যখন ইনজেকশন দিচ্ছেন fetch_order(order: OrderID) -> Order
তার অর্থ হল যে কোনও কিছু ভিতরে থাকতে পারে। requests
, ডাটাবেস, যাই হোক না কেন।
বিষয়গুলি সংক্ষিপ্ত করতে:
ইনজেকশন ব্যবহারের সুবিধা কী?
প্রধান সুবিধাটি হ'ল আপনাকে নিজের নির্ভরতাগুলি ম্যানুয়ালি একত্রিত করতে হবে না। যাইহোক, এটি একটি বিশাল ব্যয়ের সাথে আসে: আপনি সমস্যাগুলি সমাধানের জন্য জটিল, এমনকি যাদুকর, সরঞ্জামগুলি ব্যবহার করছেন। একদিন বা অন্য কোনও জটিলতা আপনাকে লড়াই করবে fight
ইনজেকশন ফ্রেমওয়ার্কটি বিরক্ত করা এবং ব্যবহার করা কি উপযুক্ত?
inject
বিশেষ করে ফ্রেমওয়ার্ক সম্পর্কে আরও একটি জিনিস । আমি যখন পছন্দ করি না এমন বস্তুগুলি যখন সম্পর্কে জানে তখন আমি পছন্দ করি না। এটি একটি বাস্তবায়ন বিস্তারিত!
একটি বিশ্বের Postcard
ডোমেন মডেলটিতে, উদাহরণস্বরূপ, এই জিনিসটি কীভাবে জানে?
আমি punq
সাধারণ ক্ষেত্রে এবং জটিলগুলির জন্য ব্যবহার করার পরামর্শ দেব dependencies
।
inject
এছাড়াও "নির্ভরতা" এবং অবজেক্ট বৈশিষ্ট্যের পরিষ্কার বিচ্ছিন্নতা প্রয়োগ করে না। যেমনটি বলা হয়েছিল, ডিআই-এর অন্যতম প্রধান লক্ষ্য হ'ল কঠোর দায়িত্ব পালন করা।
বিপরীতে, আমাকে দেখায় যে কীভাবে punq
কাজ করে:
from typing_extensions import final
from attr import dataclass
# Note, we import protocols, not implementations:
from project.postcards.repository.protocols import PostcardsForToday
from project.postcards.services.protocols import (
SendPostcardsByEmail,
CountPostcardsInAnalytics,
)
@final
@dataclass(frozen=True, slots=True)
class SendTodaysPostcardsUsecase(object):
_repository: PostcardsForToday
_email: SendPostcardsByEmail
_analytics: CountPostcardInAnalytics
def __call__(self, today: datetime) -> None:
postcards = self._repository(today)
self._email(postcards)
self._analytics(postcards)
দেখা? এমনকি আমাদের কোনও নির্মাণকারীও নেই। আমরা ঘোষকভাবে আমাদের নির্ভরতাগুলি সংজ্ঞায়িত করি এবং punq
সেগুলি স্বয়ংক্রিয়ভাবে ইনজেক্ট করব। এবং আমরা কোনও নির্দিষ্ট বাস্তবায়ন সংজ্ঞায়িত করি না। কেবল অনুসরণ করতে প্রোটোকল। এই স্টাইলটিকে "ফাংশনাল অবজেক্টস" বা এসআরপি- স্টাইল্ড ক্লাস বলা হয়।
তারপরে আমরা ধারকটি punq
নিজেই সংজ্ঞায়িত করি :
# project/implemented.py
import punq
container = punq.Container()
# Low level dependencies:
container.register(Postgres)
container.register(SendGrid)
container.register(GoogleAnalytics)
# Intermediate dependencies:
container.register(PostcardsForToday)
container.register(SendPostcardsByEmail)
container.register(CountPostcardInAnalytics)
# End dependencies:
container.register(SendTodaysPostcardsUsecase)
এবং এটি ব্যবহার করুন:
from project.implemented import container
send_postcards = container.resolve(SendTodaysPostcardsUsecase)
send_postcards(datetime.now())
দেখা? কে এবং কীভাবে এগুলি তৈরি করে তা এখন আমাদের ক্লাসগুলির কোনও ধারণা নেই। কোনও সাজসজ্জার নেই, কোনও বিশেষ মূল্য নেই।
এখানে এসআরপি-স্টাইলযুক্ত ক্লাস সম্পর্কে আরও পড়ুন:
বাইরে থেকে ডোমেনকে আলাদা করার মতো আরও ভাল কোনও উপায় আছে কি?
আপনি আবশ্যকীয়গুলির পরিবর্তে ক্রিয়ামূলক প্রোগ্রামিং ধারণাগুলি ব্যবহার করতে পারেন। ফাংশন নির্ভরতা ইনজেকশনটির মূল ধারণাটি হ'ল আপনি এমন জিনিসগুলিকে কল করবেন না যা আপনার প্রসঙ্গের উপর নির্ভর করে। প্রসঙ্গটি উপস্থিত থাকাকালীন আপনি এই কলগুলির জন্য পরবর্তী সময় নির্ধারণ করুন। আপনি কেবল সাধারণ ফাংশনগুলির সাথে নির্ভরতা ইনজেকশনটি কীভাবে চিত্রিত করতে পারেন তা এখানে:
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from words_app.logic import calculate_points
def view(request: HttpRequest) -> HttpResponse:
user_word: str = request.POST['word'] # just an example
points = calculate_points(user_words)(settings) # passing the dependencies and calling
... # later you show the result to user somehow
# Somewhere in your `word_app/logic.py`:
from typing import Callable
from typing_extensions import Protocol
class _Deps(Protocol): # we rely on abstractions, not direct values or types
WORD_THRESHOLD: int
def calculate_points(word: str) -> Callable[[_Deps], int]:
guessed_letters_count = len([letter for letter in word if letter != '.'])
return _award_points_for_letters(guessed_letters_count)
def _award_points_for_letters(guessed: int) -> Callable[[_Deps], int]:
def factory(deps: _Deps):
return 0 if guessed < deps.WORD_THRESHOLD else guessed
return factory
এই নিদর্শনটির সাথে একমাত্র সমস্যা হ'ল এটি _award_points_for_letters
রচনা করা শক্ত।
এজন্যই আমরা এই রচনাটি সহায়তা করার জন্য একটি বিশেষ মোড়ক তৈরি করেছি (এটি এর একটি অংশ returns
:
import random
from typing_extensions import Protocol
from returns.context import RequiresContext
class _Deps(Protocol): # we rely on abstractions, not direct values or types
WORD_THRESHOLD: int
def calculate_points(word: str) -> RequiresContext[_Deps, int]:
guessed_letters_count = len([letter for letter in word if letter != '.'])
awarded_points = _award_points_for_letters(guessed_letters_count)
return awarded_points.map(_maybe_add_extra_holiday_point) # it has special methods!
def _award_points_for_letters(guessed: int) -> RequiresContext[_Deps, int]:
def factory(deps: _Deps):
return 0 if guessed < deps.WORD_THRESHOLD else guessed
return RequiresContext(factory) # here, we added `RequiresContext` wrapper
def _maybe_add_extra_holiday_point(awarded_points: int) -> int:
return awarded_points + 1 if random.choice([True, False]) else awarded_points
উদাহরণস্বরূপ, খাঁটি ফাংশন দিয়ে নিজেকে রচনা করার জন্য RequiresContext
বিশেষ .map
পদ্ধতি রয়েছে । এবং এটাই. ফলস্বরূপ আপনার কাছে সহজ এপিআই সহ কেবল সাধারণ ফাংশন এবং রচনা সহায়ক have কোন জাদু নেই, কোনও অতিরিক্ত জটিলতা নেই। এবং বোনাস হিসাবে সবকিছু যথাযথভাবে টাইপ করা হয় এবং এর সাথে সামঞ্জস্যপূর্ণ mypy
।
এই পদ্ধতির সম্পর্কে এখানে আরও পড়ুন: