স্কালা কেস ক্লাস ঘোষণার অসুবিধাগুলি কী কী?


105

আপনি যদি প্রচুর সুন্দর, অপরিবর্তনীয় ডেটা স্ট্রাকচার ব্যবহার করছেন এমন কোডটি লিখছেন তবে কেস ক্লাসগুলি একটি গডসেন্ড হিসাবে উপস্থিত বলে মনে হচ্ছে, কেবলমাত্র একটি কীওয়ার্ড দিয়ে নিচের সমস্তটি নিখরচায় প্রদান করে:

  • সবই ডিফল্টরূপে অপরিবর্তনীয়
  • যাত্রীরা স্বয়ংক্রিয়ভাবে সংজ্ঞায়িত
  • ডিস্টেন্ট টু স্ট্রিং () বাস্তবায়ন
  • কমপ্লিয়েন্ট সমান () এবং হ্যাশকোড ()
  • ম্যাচিংয়ের জন্য প্রয়োগযোগ্য () পদ্ধতির সাথে কম্বিয়ন অবজেক্ট

তবে কেস ক্লাস হিসাবে অপরিবর্তনীয় ডেটা স্ট্রাকচার নির্ধারণের অসুবিধাগুলি কী কী?

এটি ক্লাস বা তার ক্লায়েন্টদের উপর কোন বিধিনিষেধ স্থাপন করে?

এমন পরিস্থিতি রয়েছে যেখানে আপনার নন-কেস ক্লাস পছন্দ করা উচিত?


এই সম্পর্কিত প্রশ্নটি দেখুন: stackoverflow.com/q/4635765/156410
ডেভিড

18
কেন এটি গঠনমূলক নয়? এই সাইটের মোডগুলি বেশ কড়া। এটিতে সম্ভাব্য সত্যের উত্তরগুলির সীমাবদ্ধ সংখ্যা রয়েছে।
এলফ

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

উত্তর:


51

একটি বড় অসুবিধা: কেস ক্লাসগুলি কেস ক্লাস বাড়াতে পারে না। এটাই সীমাবদ্ধতা।

সম্পূর্ণ সুযোগের জন্য তালিকাভুক্ত অন্যান্য সুবিধাগুলি: অনুপযুক্ত সিরিয়ালাইজেশন / ডিসরিয়ালাইজেশন, তৈরি করতে "নতুন" কীওয়ার্ড ব্যবহার করার দরকার নেই।

আমি পরিবর্তনীয় রাষ্ট্র, প্রাইভেট স্টেট বা কোনও রাজ্য (যেমন বেশিরভাগ সিঙ্গলটন উপাদান) যুক্ত বস্তুর জন্য নন-কেস ক্লাস পছন্দ করি। অন্য অনেক কিছুর জন্য কেস ক্লাস।


48
আপনি একটি কেস ক্লাস সাবক্লাস করতে পারেন। সাবক্লাসটি কেস ক্লাসও হতে পারে না - এটাই সীমাবদ্ধতা।
শেঠ তিশু

99

প্রথম ভাল বিট:

সবই ডিফল্টরূপে অপরিবর্তনীয়

হ্যাঁ, এবং আপনার varযদি এটি প্রয়োজন হয় তবে এটির ওভাররাইডও করা যেতে পারে (ব্যবহার করে )

যাত্রীরা স্বয়ংক্রিয়ভাবে সংজ্ঞায়িত

যেকোন ক্লাসে প্যারামগুলির সাথে উপসর্গ রেখে সম্ভব val

শালীন toString()বাস্তবায়ন

হ্যাঁ, খুব দরকারী, তবে প্রয়োজনে যে কোনও ক্লাসের হাতে হাতে ডাবল

অনুগত equals()এবংhashCode()

সহজ প্যাটার্ন-মিলের সাথে সংযুক্ত, এটি প্রধান কারণ যা লোকেরা কেস ক্লাস ব্যবহার করে

ম্যাচিংয়ের unapply()পদ্ধতির সাথে সঙ্গী অবজেক্ট

এক্সট্র্যাক্টর ব্যবহার করে যে কোনও ক্লাসে হাতে হাতে করা সম্ভব

এই তালিকায় ওবার-শক্তিশালী অনুলিপি পদ্ধতিও অন্তর্ভুক্ত করা উচিত, স্কেলে ২.৮ আসার অন্যতম সেরা জিনিস


তারপরে খারাপ, কেস ক্লাসগুলির সাথে কেবল কয়েকটি মুখ্য আসল বাধা আছে:

applyসংকলক-উত্পন্ন পদ্ধতি হিসাবে একই স্বাক্ষরটি ব্যবহার করে আপনি সহযোগী অবজেক্টে সংজ্ঞা দিতে পারবেন না

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

আপনি সাবক্লাস করতে পারবেন না

সত্য, যদিও কেস বর্গের পক্ষে নিজেই বংশধর হওয়া সম্ভব। একটি সাধারণ প্যাটার্ন হ'ল গাছের পাতার নোড হিসাবে কেস ক্লাস ব্যবহার করে বৈশিষ্ট্যগুলির একটি শ্রেণিবদ্ধ স্তরবিন্যাস গড়ে তোলা।

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

পণ্যের সাবক্লাস হিসাবে, কেস ক্লাসে 22 টির বেশি পরামিতি থাকতে পারে না

এই অনেকগুলি প্যারামের সাথে ক্লাস অপব্যবহার বন্ধ করা ছাড়া সত্যিকারের কাজ নেই :)

এছাড়াও ...

অন্য একটি নিষেধাজ্ঞাকে মাঝে মাঝে লক্ষ করা যায় যে স্কেলাল (বর্তমানে) অলস প্যারামগুলি সমর্থন করে না ( lazy valযেমন, তবে পরামিতি হিসাবে)। এটির মত কাজটি হ'ল একটি বাই-প্যারাম ব্যবহার করা এবং এটি কনস্ট্রাক্টরের একটি অলস ভ্যালিতে অর্পণ করা। দুর্ভাগ্যক্রমে, নাম অনুসারে প্যারামগুলি প্যাটার্ন ম্যাচিংয়ের সাথে মিশে না, যা সংকলক দ্বারা উত্পাদিত এক্সট্র্যাক্টরকে ভেঙে দেওয়ার কারণে কেস ক্লাসগুলির সাথে কৌশলটি ব্যবহার করা বাধা দেয়।

আপনি যদি উচ্চ-কার্যকরী অলস ডেটা স্ট্রাকচার বাস্তবায়ন করতে চান তবে এটি প্রাসঙ্গিক এবং আশা করি স্কালার ভবিষ্যতে প্রকাশের ক্ষেত্রে অলস প্যারাম যুক্ত করে সমাধান করা হবে।


1
ব্যাপক উত্তরের জন্য ধন্যবাদ। আমি মনে করি "আপনি সাবক্লাস করতে পারবেন না" সমস্ত ব্যতিক্রম সম্ভবত খুব শীঘ্রই আমাকে ফেজ করার সম্ভাবনা নেই।
গ্রাহাম লেয়া

15
আপনি একটি কেস ক্লাস সাবক্লাস করতে পারেন। সাবক্লাসটি কেস ক্লাসও হতে পারে না - এটাই সীমাবদ্ধতা।
শেঠ তিশু

5
কেস ক্লাসগুলির জন্য 22-প্যারামিটার সীমাটি স্কেলা 2.11-এ সরানো হয়েছে। अंक.স্কালা-lang.org/browse/SI-7296
জোনাথন ক্রসমার

এটি সংশোধন করা ভুল যে "আপনি কম্পাইলার-উত্পন্ন পদ্ধতিটির মতো একই স্বাক্ষর ব্যবহার করে সহকর্মী অবজেক্টে প্রয়োগের সংজ্ঞা দিতে পারবেন না"। যদিও এটা কিছু হুপ্স মাধ্যমে জাম্পিং প্রয়োজন তা করার (যদি আপনি কার্যকারিতা যা অলক্ষ্যে Scala কম্পাইলার দ্বারা উত্পন্ন হত রাখা মনস্থ), এটা অবশ্যই অর্জন করা যেতে পারে: stackoverflow.com/a/25538287/501113
chaotic3quilibrium

আমি স্কালার কেস ক্লাসগুলি ব্যাপকভাবে ব্যবহার করছি এবং একটি "কেস ক্লাস প্যাটার্ন" নিয়ে এসেছি (যা শেষ পর্যন্ত স্ক্যালাল ম্যাক্রো হিসাবে শেষ হবে) যা উপরে চিহ্নিত বেশ কয়েকটি সমস্যার সাথে সহায়তা করে: কোডরেভিউ.স্ট্যাকেক্সেক্সঞ্জ
বিশৃঙ্খলা

10

আমি মনে করি টিডিডি নীতিটি এখানে প্রয়োগ হয়: অতিরিক্ত নকশা করবেন না। আপনি যখন case classকোনওটিকে একটি হিসাবে ঘোষণা করেন, আপনি প্রচুর কার্যকারিতা ঘোষণা করছেন। এটি ভবিষ্যতে শ্রেণি পরিবর্তন করতে আপনার নমনীয়তা হ্রাস করবে।

উদাহরণস্বরূপ, কনস্ট্রাক্টর পরামিতিগুলির উপর একটির case classএকটি equalsপদ্ধতি রয়েছে। আপনি যখন প্রথমে আপনার ক্লাসটি লিখবেন তখন আপনি সেটির দিকে খেয়াল রাখতে পারেন না, তবে, পরে সিদ্ধান্ত নিতে পারেন আপনি এই প্যারামিটারগুলির কিছু উপেক্ষা করে সামান্য কিছু চান বা কিছু আলাদা করতে পারেন want তবে ক্লায়েন্ট কোডটি মাঝের সময়ে লিখিত হতে পারে যা case classসাম্যের উপর নির্ভর করে ।


4
আমি মনে করি না ক্লায়েন্ট কোড 'সমান' এর সঠিক অর্থের উপর নির্ভর করে; এটি 'সমান' এর অর্থ কী তা সিদ্ধান্ত নেওয়া কোনও শ্রেণীর উপর নির্ভর করে। শ্রেণি লেখকের লাইনটি নীচে 'সমান' এর প্রয়োগ পরিবর্তন করতে অবাধ হওয়া উচিত।
পকেডিং

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

3
@ ড্যানিয়েলসি.সোব্রাল ট্রু, তবে সমান () এর ক্ষেত্রগুলি (এটি ভিত্তিতে ভিত্তি করে) এর সঠিক বাস্তবায়ন চুক্তিতে অগত্যা নয়। কমপক্ষে, আপনি প্রথম ক্লাসটি লেখার সময় চুক্তি থেকে এটি স্পষ্টভাবে বাদ দিতে পারেন।
Herman

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

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

7

এমন পরিস্থিতি রয়েছে যেখানে আপনার নন-কেস ক্লাস পছন্দ করা উচিত?

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

বলুন, আমরা গাণিতিক প্রকাশের জন্য একটি দোভাষী লিখতে চাই। শুরুতে জিনিসগুলি সহজ রাখতে, আমরা কেবলমাত্র সংখ্যা এবং + ক্রিয়াকলাপের মধ্যে সীমাবদ্ধ করি। এই জাতীয় এক্সপ্রেশনগুলি শ্রেণীর শ্রেণিবিন্যাস হিসাবে উপস্থাপন করা যেতে পারে, মূল হিসাবে একটি বিমূর্ত বেস শ্রেণি এক্সপ্রেস এবং দুটি উপশ্রেন সংখ্যা এবং যোগফল। তারপরে, একটি অভিব্যক্তি 1 + (3 + 7) হিসাবে উপস্থাপিত হবে

নতুন যোগফল (নতুন সংখ্যা (1), নতুন যোগফল (নতুন সংখ্যা (3), নতুন সংখ্যা (7))

abstract class Expr {
  def eval: Int
}

class Number(n: Int) extends Expr {
  def eval: Int = n
}

class Sum(e1: Expr, e2: Expr) extends Expr {
  def eval: Int = e1.eval + e2.eval
}

তদুপরি, একটি নতুন প্রোড ক্লাস যুক্ত করা বিদ্যমান কোডে কোনও পরিবর্তন প্রেরণা দেয় না:

class Prod(e1: Expr, e2: Expr) extends Expr {
  def eval: Int = e1.eval * e2.eval
}

বিপরীতে, একটি নতুন পদ্ধতি যুক্ত করতে সমস্ত বিদ্যমান ক্লাসগুলির সংশোধন প্রয়োজন।

abstract class Expr { 
  def eval: Int 
  def print
} 

class Number(n: Int) extends Expr { 
  def eval: Int = n 
  def print { Console.print(n) }
}

class Sum(e1: Expr, e2: Expr) extends Expr { 
  def eval: Int = e1.eval + e2.eval
  def print { 
   Console.print("(")
   print(e1)
   Console.print("+")
   print(e2)
   Console.print(")")
  }
}

কেস ক্লাসগুলির সাথে একই সমস্যা সমাধান করা।

abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
  }
}
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr

একটি নতুন পদ্ধতি যুক্ত করা একটি স্থানীয় পরিবর্তন।

abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
  }
  def print = this match {
    case Number(n) => Console.print(n)
    case Sum(e1,e2) => {
      Console.print("(")
      print(e1)
      Console.print("+")
      print(e2)
      Console.print(")")
    }
  }
}

একটি নতুন প্রোড ক্লাস যুক্ত করার জন্য সমস্ত প্যাটার্নের মিলটি সম্ভাব্যভাবে পরিবর্তনের প্রয়োজন।

abstract class Expr {
  def eval: Int = this match {
    case Number(n) => n
    case Sum(e1, e2) => e1.eval + e2.eval
    case Prod(e1,e2) => e1.eval * e2.eval
  }
  def print = this match {
    case Number(n) => Console.print(n)
    case Sum(e1,e2) => {
      Console.print("(")
      print(e1)
      Console.print("+")
      print(e2)
      Console.print(")")
    }
    case Prod(e1,e2) => ...
  }
}

ভিডিও চিত্র থেকে প্রতিলিপি 4.6 প্যাটার্ন ম্যাচিং

এই উভয় ডিজাইনই পুরোপুরি সূক্ষ্ম এবং এগুলির মধ্যে পছন্দগুলি কখনও কখনও শৈলীর বিষয় হয় তবে এরপরেও কিছু মানদণ্ড রয়েছে যা গুরুত্বপূর্ণ।

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

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

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

সুতরাং এই এক্সটেনসিবিলিটির সমস্যাটি দুটি মাত্রায়, যেখানে আপনি একটি শ্রেণিবিন্যাসে নতুন ক্লাস যুক্ত করতে চাইতে পারেন বা আপনি নতুন পদ্ধতি যুক্ত করতে চাইতে পারেন বা উভয়ই হতে পারে অভিব্যক্তির সমস্যা হিসাবে

মনে রাখবেন: আমাদের অবশ্যই এটি একটি প্রাথমিক পয়েন্টের মতো ব্যবহার করতে হবে, কেবলমাত্র মানদণ্ডের মতো নয়।

এখানে চিত্র বর্ণনা লিখুন


0

আমি থেকে এই উদ্ধৃতি করছি Scala cookbookদ্বারা Alvin Alexanderঅধ্যায় 6: objects

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

কেস ক্লাসের জন্য একাধিক কনস্ট্রাক্টর সরবরাহ করতে কেস ক্লাসের ঘোষণাটি আসলে কী করে তা জেনে রাখা গুরুত্বপূর্ণ।

case class Person (var name: String)

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

$ javap Person$
Compiled from "Person.scala"
public final class Person$ extends scala.runtime.AbstractFunction1 implements scala.ScalaObject,scala.Serializable{
public static final Person$ MODULE$;
public static {};
public final java.lang.String toString();
public scala.Option unapply(Person);
public Person apply(java.lang.String); // the apply method (returns a Person) public java.lang.Object readResolve();
        public java.lang.Object apply(java.lang.Object);
    }

এতে পার্সন.ক্লাস কী রয়েছে তা দেখতে আপনি আলাদা করতে পারেন। এর মতো সরল শ্রেণীর জন্য এটিতে অতিরিক্ত 20 টি পদ্ধতি রয়েছে; এই লুকানো ফোটা একটি কারণ যা কিছু বিকাশকারী কেস ক্লাস পছন্দ করে না।

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