নির্ভরতা ইনজেকশনের জন্য পাঠক মোনাড: একাধিক নির্ভরতা, নেস্টেড কল


87

স্কেলে নির্ভরতা ইনজেকশন সম্পর্কে জিজ্ঞাসা করা হলে, বেশিরভাগ উত্তর পাঠক মোনাড ব্যবহারের দিকে নির্দেশ করে, স্ক্যালাজের যে কোনও একটি বা কেবল আপনার নিজের ঘূর্ণায়মান। পদ্ধতির মূল বিষয়গুলি বর্ণনা করার জন্য বেশ কয়েকটি সুস্পষ্ট নিবন্ধ রয়েছে (উদাঃ রুনার আলাপ , জেসনের ব্লগ ), তবে আমি এর চেয়ে আরও সম্পূর্ণ উদাহরণ খুঁজে বের করতে পারি নি এবং আমি এই পদ্ধতির সুবিধাগুলি দেখতে ব্যর্থ হয়েছি যেমন আরও একটি traditionalতিহ্যবাহী "ম্যানুয়াল" ডিআই ( আমার লেখা গাইডটি দেখুন )। সম্ভবত আমি কিছু গুরুত্বপূর্ণ বিষয় অনুপস্থিত, তাই প্রশ্ন।

একটি উদাহরণ হিসাবে, আসুন কল্পনা করুন যে আমাদের এই ক্লাসগুলি রয়েছে:

trait Datastore { def runQuery(query: String): List[String] }
trait EmailServer { def sendEmail(to: String, content: String): Unit }

class FindUsers(datastore: Datastore) {
  def inactive(): Unit = ()
}

class UserReminder(findUser: FindUsers, emailServer: EmailServer) {
  def emailInactive(): Unit = ()
}

class CustomerRelations(userReminder: UserReminder) {
  def retainUsers(): Unit = {}
}

এখানে আমি ক্লাস এবং কনস্ট্রাক্টর পরামিতি ব্যবহার করে জিনিসগুলির মডেলিং করছি, যা "traditionalতিহ্যবাহী" ডিআই পদ্ধতির সাথে খুব সুন্দরভাবে খেলছে, তবে এই নকশায় বেশ কয়েকটি ভাল দিক রয়েছে:

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

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

আমি কিছুটা সম্পর্কিত প্রশ্ন পেয়েছি যা এর পরামর্শ দেয়:

  • সমস্ত নির্ভরতা সহ একক পরিবেশের অবজেক্ট ব্যবহার করা
  • স্থানীয় পরিবেশ ব্যবহার করে
  • "পারফাইট" প্যাটার্ন
  • টাইপ-ইনডেক্সড মানচিত্র

যাইহোক, এই সাধারণ সমাধান হিসাবে কিছুটা জটিল হলেও (তবে এটি বিষয়গত) কিছুটা জটিল না হওয়া ছাড়াও, এই সমস্ত সমাধানে যেমন retainUsersপদ্ধতি (যা কল করে emailInactive, যা inactiveনিষ্ক্রিয় ব্যবহারকারীদের সন্ধান করার জন্য আহ্বান জানায় ) Datastoreনির্ভরতা সম্পর্কে জানতে হবে , নেস্টেড ফাংশনগুলি সঠিকভাবে কল করতে সক্ষম হবেন - বা আমি ভুল করছি?

এই জাতীয় "ব্যবসায়িক অ্যাপ্লিকেশন" এর জন্য পাঠক মোনাডকে কোন দিক দিয়ে কেবল কনস্ট্রাক্টর প্যারামিটার ব্যবহার না করা ভাল?


4
পাঠক মনাদ কোনও রূপোর বুলেট নয়। আমি মনে করি, আপনার যদি নির্ভরশীলতার অনেক স্তর প্রয়োজন হয় তবে আপনার নকশাটি বেশ ভাল।
ZhekaKozlov

তবে প্রায়শই এটি নির্ভরতা ইনজেকশনের বিকল্প হিসাবে বর্ণনা করা হয়; সম্ভবত এটি তখন পরিপূরক হিসাবে বর্ণনা করা উচিত? আমি মাঝে মাঝে এই অনুভূতিটি পাই যে ডিআই "সত্যিকারের ক্রিয়ামূলক প্রোগ্রামারস" দ্বারা বরখাস্ত হয়ে গেছে, তাই আমি "কী পরিবর্তে" তা ভাবছিলাম :) যেভাবেই হোক, আমি মনে করি একাধিক স্তরের নির্ভরতা রয়েছে, বা একাধিক বাহ্যিক পরিষেবা যা আপনার সাথে কথা বলা উচিত তা কীভাবে হয় প্রতিটি মাঝারি-বড় "ব্যবসায়িক অ্যাপ্লিকেশন" দেখতে দেখতে (নিশ্চিতভাবে লাইব্রেরিগুলির ক্ষেত্রে নয়)
অ্যাডামডাব্লু

4
আমি সর্বদা স্থানীয় কিছু হিসাবে রিডার মোনাড সম্পর্কে ভাবা হয়েছিল। উদাহরণস্বরূপ, আপনার যদি এমন কোনও মডিউল থাকে যা কেবল একটি ডিবি-তে কথা বলে তবে আপনি এই মডিউলটি রিডার মোনাড স্টাইলে প্রয়োগ করতে পারেন। তবে, যদি আপনার অ্যাপ্লিকেশনটিতে অনেকগুলি বিভিন্ন ডেটা উত্সের প্রয়োজন হয় যা একত্রে একত্রিত করা উচিত, আমি মনে করি না যে রিডার মোনাড সেটির জন্য ভাল।
heেকা কোজলভ

আহ, এটি দুটি ধারণাটিকে কীভাবে একত্রিত করা যায় এটি একটি ভাল নির্দেশিকা হতে পারে। এবং তারপরে সত্যই এটি ডিআই এবং আরএম একে অপরের পরিপূরক বলে মনে হবে। আমি অনুমান করি যে এটি কেবলমাত্র একটি নির্ভরশীলতার উপর নির্ভরশীল ফাংশনগুলি রাখা খুব সাধারণ বিষয় এবং এখানে আরএম ব্যবহার করা নির্ভরতা / ডেটা সীমানা স্পষ্ট করতে সহায়তা করবে।
23:55

উত্তর:


37

এই উদাহরণটি কীভাবে মডেল করবেন

এটি কীভাবে পাঠকের মনডের সাথে মডেল করা যায়?

আমি নিশ্চিত নই যে এটি পাঠকের সাথে মডেল করা উচিত কিনা, তবুও এটি হতে পারে:

  1. ক্লাসগুলিকে ফাংশন হিসাবে এনকোডিং করা যা কোডটি প্লেয়ারের সাথে আরও সুন্দর করে তোলে
  2. পাঠকের সাথে বোঝার জন্য এবং এটি ব্যবহারের জন্য ফাংশনগুলি রচনা করা

শুরুর ঠিক ঠিক আগে আমাকে ছোট্ট নমুনা কোড সামঞ্জস্য সম্পর্কে আপনাকে জানাতে হবে যা আমি এই উত্তরের জন্য উপকারী বলে মনে করেছি। প্রথম পরিবর্তনটি FindUsers.inactiveপদ্ধতি সম্পর্কে । আমি এটিকে ফিরে আসতে List[String]দিলাম যাতে ঠিকানাগুলির তালিকাটি UserReminder.emailInactiveপদ্ধতিতে ব্যবহার করা যায় । আমি পদ্ধতিগুলিতে সহজ বাস্তবায়নও যুক্ত করেছি। শেষ অবধি, নমুনাটি হ'ল পাঠক মোনাডের হ্যান্ড-রোলড সংস্করণটি ব্যবহার করবে:

case class Reader[Conf, T](read: Conf => T) { self =>

  def map[U](convert: T => U): Reader[Conf, U] =
    Reader(self.read andThen convert)

  def flatMap[V](toReader: T => Reader[Conf, V]): Reader[Conf, V] =
    Reader[Conf, V](conf => toReader(self.read(conf)).read(conf))

  def local[BiggerConf](extractFrom: BiggerConf => Conf): Reader[BiggerConf, T] =
    Reader[BiggerConf, T](extractFrom andThen self.read)
}

object Reader {
  def pure[C, A](a: A): Reader[C, A] =
    Reader(_ => a)

  implicit def funToReader[Conf, A](read: Conf => A): Reader[Conf, A] =
    Reader(read)
}

মডেলিং পদক্ষেপ 1. ফাংশন হিসাবে ক্লাস এনকোডিং

সম্ভবত এটি optionচ্ছিক, আমি নিশ্চিত নই, তবে পরে এটি বোঝার জন্য আরও ভাল দেখায়। দ্রষ্টব্য, যে ফলস্বরূপ ফাংশন তীক্ষ্ণ হয় এটি প্রাক্তন কনস্ট্রাক্টর আর্গুমেন্টকে তাদের প্রথম প্যারামিটার (পরামিতি তালিকা) হিসাবে গ্রহণ করে। ঐ দিকে

class Foo(dep: Dep) {
  def bar(arg: Arg): Res = ???
}
// usage: val result = new Foo(dependency).bar(arg)

হয়ে যায়

object Foo {
  def bar: Dep => Arg => Res = ???
}
// usage: val result = Foo.bar(dependency)(arg)

মনে রাখবেন যে প্রতিটি Dep, Arg, Resধরনের সম্পূর্ণরূপে অবাধ হতে পারে: একটি tuple, একটি ফাংশান বা একটা সহজ প্রকার।

প্রাথমিক সমন্বয়ের পরে নমুনা কোডটি এখানে ফাংশনে রূপান্তরিত হয়েছে:

trait Datastore { def runQuery(query: String): List[String] }
trait EmailServer { def sendEmail(to: String, content: String): Unit }

object FindUsers {
  def inactive: Datastore => () => List[String] =
    dataStore => () => dataStore.runQuery("select inactive")
}

object UserReminder {
  def emailInactive(inactive: () => List[String]): EmailServer => () => Unit =
    emailServer => () => inactive().foreach(emailServer.sendEmail(_, "We miss you"))
}

object CustomerRelations {
  def retainUsers(emailInactive: () => Unit): () => Unit =
    () => {
      println("emailing inactive users")
      emailInactive()
    }
}

এখানে লক্ষ্য করার একটি বিষয় হ'ল নির্দিষ্ট ফাংশনগুলি পুরো অবজেক্টের উপর নির্ভর করে না, তবে কেবল প্রত্যক্ষভাবে ব্যবহৃত অংশগুলিতে। ওওপি সংস্করণে UserReminder.emailInactive()উদাহরণটি userFinder.inactive()এখানে কল করবে যেখানে এটি কেবল কল করে inactive() - প্রথম প্যারামিটারে কোনও ফাংশন এটিতে চলে গেছে।

দয়া করে নোট করুন, কোডটি প্রশ্ন থেকে তিনটি পছন্দসই বৈশিষ্ট্য দেখায়:

  1. প্রতিটি কার্যকারিতা কী ধরণের নির্ভরশীলতার প্রয়োজন তা স্পষ্ট
  2. অন্যের থেকে একটি কার্যকারিতার নির্ভরতা আড়াল করে
  3. retainUsers পদ্ধতিটি ডেটাস্টোর নির্ভরতা সম্পর্কে জানতে হবে না

মডেলিং পদক্ষেপ 2 ফাংশন রচনা এবং এগুলি চালনার জন্য পাঠক ব্যবহার করে

পাঠক মনাদ আপনাকে কেবলমাত্র ফাংশন রচনা করতে দেয় যা সমস্ত একই ধরণের উপর নির্ভর করে। এটি প্রায়শই একটি মামলা হয় না। আমাদের উদাহরণ FindUsers.inactiveউপর Datastoreএবং UserReminder.emailInactiveউপর নির্ভর করে EmailServer। এই সমস্যাটি সমাধানের জন্য কেউ একটি নতুন প্রকার (প্রায়শই কনফিগার হিসাবে পরিচিত) প্রবর্তন করতে পারে যার মধ্যে সমস্ত নির্ভরতা থাকে তবে ফাংশনগুলি পরিবর্তন করুন যাতে তারা সকলেই এর উপর নির্ভর করে এবং কেবল এটি থেকে প্রাসঙ্গিক ডেটা গ্রহণ করে। এটি নির্ভরশীলতা পরিচালনার দৃষ্টিকোণ থেকে স্পষ্টতই ভুল কারণ আপনি এই ফাংশনগুলিকে এমন ধরণের উপরও নির্ভরশীল করেন যেগুলি সম্পর্কে তারা প্রথম স্থানেই জানেন না।

ভাগ্যক্রমে এটি সক্রিয়, এটি ফাংশনটি কাজ করার একটি উপায় রয়েছে Configএমনকি যদি এটি প্যারামিটার হিসাবে কেবল এর কিছু অংশ গ্রহণ করে। এটি একটি পদ্ধতি বলা হয় local, এটি পাঠকের সংজ্ঞায়িত। এটি থেকে প্রাসঙ্গিক অংশটি বের করার একটি উপায় সরবরাহ করা প্রয়োজন Config

এই জ্ঞানটি হাতের উদাহরণটিতে প্রয়োগ হয়েছে যা দেখতে এরকম হবে:

object Main extends App {

  case class Config(dataStore: Datastore, emailServer: EmailServer)

  val config = Config(
    new Datastore { def runQuery(query: String) = List("john.doe@fizzbuzz.com") },
    new EmailServer { def sendEmail(to: String, content: String) = println(s"sending [$content] to $to") }
  )

  import Reader._

  val reader = for {
    getAddresses <- FindUsers.inactive.local[Config](_.dataStore)
    emailInactive <- UserReminder.emailInactive(getAddresses).local[Config](_.emailServer)
    retainUsers <- pure(CustomerRelations.retainUsers(emailInactive))
  } yield retainUsers

  reader.read(config)()

}

কনস্ট্রাক্টর প্যারামিটার ব্যবহারের ক্ষেত্রে সুবিধা

এই জাতীয় "ব্যবসায়িক অ্যাপ্লিকেশন" এর জন্য পাঠক মোনাডকে কোন দিক দিয়ে কেবল কনস্ট্রাক্টর প্যারামিটার ব্যবহার না করা ভাল?

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

  1. ইউনিফর্মিটি - বোঝার জন্য কতটা সংক্ষিপ্ত / দীর্ঘ তা কোন ম্যাটর নয়, এটি কেবল একটি পাঠক এবং আপনি এটি সহজেই অন্য একটি উদাহরণ দিয়ে রচনা করতে পারেন, সম্ভবত কেবল আরও একটি কনফিগার ধরণের পরিচয় করিয়ে দেওয়া হয়েছে এবং এর localউপরে কয়েকটি কল ছিটানো রয়েছে । এই বিন্দুটি আইএমও বরং স্বাদের বিষয়, কারণ আপনি যখন কনস্ট্রাক্টর ব্যবহার করেন তখন কেউ আপনার পছন্দ মতো জিনিসগুলি রচনা করতে বাধা দেয় না, যদি না কেউ নির্বোধের মতো কাজ করেন, যেমন ওওপিতে খারাপ অভ্যাস হিসাবে বিবেচিত হয়।
  2. পাঠক, একটি একসংখ্যা তাই এটা যে এর সাথে সম্পর্কিত সকল সুবিধা পায় - sequence, traverseপদ্ধতি বিনামূল্যে জন্য প্রয়োগ।
  3. কিছু ক্ষেত্রে আপনি কেবল একবারে পাঠক তৈরি করা ভাল এবং বিস্তৃত কনফিগের জন্য এটি ব্যবহার করতে পারেন। কনস্ট্রাক্টরগুলির সাথে কেউ আপনাকে এটি করতে বাধা দেয় না, আপনাকে প্রতিটি কনফিগার ইনকামিংয়ের জন্য নতুন অবজেক্ট গ্রাফটি নতুন করে তৈরি করতে হবে। যদিও এতে আমার কোনও সমস্যা নেই (আমি আবেদন করার প্রতিটি অনুরোধের ভিত্তিতে এটি করাও পছন্দ করি), কারণ যে কারণে আমি কেবলমাত্র অনুমান করতে পারি তা অনেক লোকের কাছে এটি সুস্পষ্ট ধারণা নয়।
  4. পাঠক আপনাকে আরও বেশি কার্যকারিতা ব্যবহারের দিকে ঠেলে দেয় যা মূলত এফপি স্টাইলে লিখিত প্রয়োগের সাথে আরও ভাল খেলবে।
  5. পাঠক উদ্বেগ আলাদা করে; আপনি নির্ভরতা সরবরাহ না করে লজিক সংজ্ঞায়িত করতে পারবেন, সবকিছু দিয়ে ইন্টারঅ্যাক্ট করতে পারবেন। আসলে সরবরাহ পরে, পৃথকভাবে। (এই বিষয়টির জন্য ধন্যবাদ কেন স্ক্র্যামব্লার) Thanks এটি প্রায়শই পাঠকের সুবিধার জন্য শোনা যায়, তবু এটি সরল নির্মাতাদের দ্বারাও সম্ভব with

আমি রিডারে যা পছন্দ করি না তাও বলতে চাই।

  1. বিপণন। কখনও কখনও আমি অনুভব করি যে রিডারটি সমস্ত ধরণের নির্ভরতার জন্য বাজারজাত করা হয়, পার্থক্য ছাড়াই যদি সে সেশন কুকি বা ডেটাবেস থাকে। আমার কাছে এই উদাহরণ থেকে ইমেল সার্ভার বা সংগ্রহস্থলের মতো ব্যবহারিকভাবে ধ্রুবক অবজেক্টের জন্য রিডার ব্যবহার করার সামান্য জ্ঞান নেই। এই ধরনের নির্ভরতার জন্য আমি প্লেইন কনস্ট্রাক্টর এবং / অথবা আংশিকভাবে প্রয়োগ ফাংশনগুলি আরও ভালভাবে পাই। মূলত রিডার আপনাকে নমনীয়তা দেয় যাতে আপনি প্রতিটি কলে আপনার নির্ভরতা নির্দিষ্ট করতে পারেন, তবে আপনার যদি সত্যিই এটির প্রয়োজন না হয় তবে আপনি কেবল তার করটি প্রদান করেন।
  2. অন্তর্নিহিত ভারাক্রান্ততা - জড়িত ছাড়াই রিডার ব্যবহার করা উদাহরণটিকে পড়া শক্ত করে তোলে। অন্যদিকে, আপনি যখন ছদ্মবেশ ব্যবহার করে কোলাহলপূর্ণ অংশগুলি আড়াল করেন এবং কিছু ত্রুটি করেন, সংকলক মাঝে মাঝে আপনাকে ডেসিফার বার্তাগুলি বোঝা শক্ত করে দেয়।
  3. এর সাথে অনুষ্ঠান করা pure, localএবং নিজস্ব কনফিগার ক্লাস তৈরি করা / এর জন্য টিপলস ব্যবহার করে। পাঠক আপনাকে এমন কিছু কোড যুক্ত করতে বাধ্য করেন যা সমস্যা ডোমেন সম্পর্কিত নয়, সুতরাং কোডটিতে কিছু শব্দ শোনার জন্য। অন্যদিকে, একটি অ্যাপ্লিকেশন যা কনস্ট্রাক্টর ব্যবহার করে প্রায়শই কারখানার প্যাটার্ন ব্যবহার করে, যা সমস্যা ডোমেনের বাইরের থেকেও তাই, এই দুর্বলতা এতটা গুরুতর নয়।

যদি আমি আমার ক্লাসগুলি ফাংশন সহ বস্তুতে রূপান্তর করতে না চাই তবে কী হবে?

তুমি চাও. আপনি প্রযুক্তিগতভাবে এড়াতে পারেন , তবে দেখুন যদি আমি FindUsersক্লাসকে অবজেক্টে রূপান্তর না করি তবে কী হবে look বোঝার জন্য সম্পর্কিত লাইনটি দেখতে পাবেন:

getAddresses <- ((ds: Datastore) => new FindUsers(ds).inactive _).local[Config](_.dataStore)

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


বিস্তারিত উত্তরের জন্য ধন্যবাদ :) একটি বিষয় যা আমার কাছে পরিষ্কার নয়, তা কেন Datastoreএবং EmailServerবৈশিষ্ট্য হিসাবে রেখে দেওয়া হয়, এবং অন্যরা objectএস হয়ে যায় ? এই পরিষেবাগুলি / নির্ভরতা / (তবে আপনি তাদের কল করুন) এর মধ্যে কি কোনও মৌলিক পার্থক্য রয়েছে যার কারণে তাদের সাথে আলাদা আচরণ করা হয়?
অ্যাডামডাব্লু

আচ্ছা ... আমি যেমন EmailSenderকোনও বস্তুতে যেমন রূপান্তর করতে পারি না , তাই না? আমি তখন টাইপ না করে নির্ভরতা প্রকাশ করতে সক্ষম হবো না ...
অ্যাডামডাব্লু

আহ, নির্ভরতা তারপরে একটি উপযুক্ত টাইপ সহ একটি ফাংশন রূপ নেবে - সুতরাং টাইপ নাম ব্যবহার না করে সবকিছু ফাংশনের স্বাক্ষরে (নামটি কেবল ঘটনাচক্রে) প্রবেশ করতে হবে। হতে পারে, তবে আমি নিশ্চিত নই;)
অ্যাডাম

সঠিক। EmailSenderআপনি নির্ভর করতে চাই তার উপর নির্ভর করে (String, String) => Unit। এটি নিশ্চিত হওয়া বা না হওয়া অন্য সমস্যা নয় :) নিশ্চিত হওয়া, এটি কমপক্ষে আরও জেনেরিক, যেহেতু প্রত্যেকে ইতিমধ্যে নির্ভর করে Function2
প্রেজেক পোক্রিউকা

আচ্ছা আপনি অবশ্যই নামটি লিখতে চাইছেন (String, String) => Unitযাতে এটি কিছু অর্থ বোঝায়, যদিও এটি কোনও টাইপ ওরফে নয় বরং এমন কিছু যা সংকলন-সময় পরীক্ষা করা হয়েছিল;)
অ্যাডামডব

3

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

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


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

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

4
আচ্ছা হ্যাঁ তবে এটি ধরে নেয় যে আপনি যে পাঠক মনাদ ব্যবহার করছেন তা প্যারামিট্রিসড Configরয়েছে যার সাথে একটি উল্লেখ রয়েছে UserRepository। সুতরাং সত্য, এটি স্বাক্ষরটিতে সরাসরি দৃশ্যমান নয়, তবে আমি বলব এটি আরও খারাপ, আপনার কোডটি কোন নজরে আপনার নজরে ব্যবহার করছে তা কোন ধারণা নেই have একটি উপর নির্ভরশীল নয় হচ্ছে না Configসব নির্ভরতা প্রতিটি পদ্ধতির ধরনের গড় উপর নির্ভরশীল এর সব তাদের?
adamw

এটি তাদের উপর নির্ভর করে তবে এটি এটি জানতে হবে না। ক্লাস সহ আপনার উদাহরণ হিসাবে একই। আমি তাদের দেখতে মোটামুটি সমতুল্য :-)
ড্যানিয়েল ল্যাংডন

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

1

গৃহীত উত্তরটি পাঠক মোনাড কীভাবে কাজ করে তার দুর্দান্ত ব্যাখ্যা সরবরাহ করে।

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

আমরা রচনা করতে চাই যে দুটি ফাংশন সংজ্ঞায়িত করা যাক: ফাংশন গ্রহণযোগ্য উত্তরে সংজ্ঞায়িত অনুরূপ।

  1. ফাংশন নির্ভর করে এমন সংস্থানগুলি সংজ্ঞায়িত করুন
  case class DataStore()
  case class EmailServer()
  1. DataStoreনির্ভরতা সহ প্রথম ফাংশনটি সংজ্ঞায়িত করুন । এটি DataStoreনিষ্ক্রিয় ব্যবহারকারীদের একটি তালিকা নেয় এবং ফেরত দেয়
  def f1(db:DataStore):List[String] = List("john@test.com", "james@test.com", "maria@test.com")
  1. EmailServerনির্ভরতা এক হিসাবে অন্য ফাংশন সংজ্ঞায়িত করুন
  def f2_raw(emailServer: EmailServer, usersToEmail:List[String]):Unit =

    usersToEmail.foreach(user => println(s"emailing ${user} using server ${emailServer}"))

এখন দুটি ফাংশন রচনা করার রেসিপিটি

  1. প্রথমে বিড়ালদের গ্রন্থাগার থেকে পাঠক আমদানি করুন
  import cats.data.Reader
  1. দ্বিতীয় ফাংশনটি পরিবর্তন করুন যাতে এটির কেবল একটি নির্ভরতা থাকে।
  val f2 = (server:EmailServer) => (usersToEmail:List[String]) => f2_raw(server, usersToEmail)

এখন f2গ্রহণ করে EmailServerএবং অন্য একটি ফাংশন প্রদান করে যা বেশিরভাগ Listব্যবহারকারীকে ইমেল করতে নেয়

  1. CombinedConfigদুটি ফাংশনের জন্য নির্ভরশীলতা রয়েছে এমন একটি শ্রেণী তৈরি করুন
  case class CombinedConfig(dataStore:DataStore, emailServer: EmailServer)
  1. 2 টি ফাংশন ব্যবহার করে পাঠক তৈরি করুন
  val r1 = Reader(f1)
  val r2 = Reader(f2)
  1. পাঠকদের পরিবর্তন করুন যাতে তারা সম্মিলিত কনফিগারেশনের সাথে কাজ করতে পারে
  val r1g = r1.local((c:CombinedConfig) => c.dataStore)
  val r2g = r2.local((c:CombinedConfig) => c.emailServer)
  1. পাঠক রচনা করুন
  val composition = for {
    u <- r1g
    e <- r2g
  } yield e(u)
  1. পাস CombinedConfigএবং রচনা প্রার্থনা
  val myConfig = CombinedConfig(DataStore(), EmailServer())

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