জাভা 8-এ, পদ্ধতি রেফারেন্স এক্সপ্রেশন বা কার্যকরী ইন্টারফেসের একটি বাস্তবায়ন ফিরিয়ে দেওয়া পদ্ধতিগুলি ব্যবহার করা কি স্টাইলিস্টিকভাবে ভাল?


11

জাভা 8 ফাংশনাল ইন্টারফেসের ধারণা যুক্ত করেছে , পাশাপাশি কার্যকরী ইন্টারফেস গ্রহণের জন্য ডিজাইন করা অসংখ্য নতুন পদ্ধতি। এই ইন্টারফেসগুলির উদাহরণগুলি পদ্ধতি রেফারেন্স এক্সপ্রেশন (উদাঃ SomeClass::someMethod) এবং ল্যাম্বডা এক্সপ্রেশন (উদাঃ (x, y) -> x + y) ব্যবহার করে সংক্ষিপ্তভাবে তৈরি করা যেতে পারে ।

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

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

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

আমার সহকর্মীর পছন্দেরটি এমন একটি পদ্ধতি থাকা উচিত যা ইন্টারফেসের উদাহরণটি ফেরত দেয় (উদাঃ "ভবিষ্যদ্বাণী ফলাফল-ভবিষ্যত ()")। আমার কাছে এটি অনুভব করে যে বৈশিষ্ট্যটি কীভাবে ব্যবহারের উদ্দেশ্যে করা হয়েছিল ঠিক তা নয়, কিছুটা ক্লঙ্কিয়ার অনুভূত হয় এবং মনে হয় নামকরণের মাধ্যমে অভিপ্রায়টি সত্যই যোগাযোগ করা শক্ত er

এই উদাহরণটি কংক্রিট করতে, এখানে একই কোডটি, বিভিন্ন স্টাইলে লিখিত:

public class ResultProcessor {
  public void doSomethingImportant(List<Result> results) {
    results.filter(this::isResultInFuture).forEach({ result ->
      // Do something important with each future result line
    });
  }

  private boolean isResultInFuture(Result result) {
    someOtherService.getResultDateFromDatabase(result).after(new Date());
  }
}

বনাম

public class ResultProcessor {
  public void doSomethingImportant(List<Result> results) {
    results.filter(resultInFuture()).forEach({ result ->
      // Do something important with each future result line
    });
  }

  private Predicate<Result> resultInFuture() {
    return result -> someOtherService.getResultDateFromDatabase(result).after(new Date());
  }
}

বনাম

public class ResultProcessor {
  public void doSomethingImportant(List<Result> results) {
    Predicate<Result> resultInFuture = result -> someOtherService.getResultDateFromDatabase(result).after(new Date());

    results.filter(resultInFuture).forEach({ result ->
      // Do something important with each future result line
    });
  }
}

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


1
ল্যাম্বডাসকে একটি কারণে ভাষায় যুক্ত করা হয়েছিল, এবং এটি জাভা আরও খারাপ করার পক্ষে নয়।

@ স্নোম্যান এটি না বলে চলে। তাই আমি ধরে নিয়েছি আপনার মন্তব্য এবং আমার প্রশ্নের মধ্যে কিছু অন্তর্নিহিত সম্পর্ক রয়েছে যা আমি যথেষ্ট ধরছি না। আপনি কী পয়েন্টটি বানাতে চাইছেন?
এম জাস্টিন

2
আমি ধরে নিচ্ছি যে তিনি যে বক্তব্যটি তৈরি করছেন তা হ'ল ল্যাম্বডাস যদি স্থিতাবস্থায় উন্নতি না করত তবে তারা তাদের পরিচয় করিয়ে বিরক্ত করত না।
রবার্ট হার্ভে

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

উত্তর:


8

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

Predicate<Result> pred = result -> this.isResultInFuture(result);
Predicate<Result> pred = this::isResultInFuture;

এগুলি অপারেশনগতভাবে সমতুল্য এবং প্রথমটিকে পয়েন্টফুল স্টাইল বলা হয় তবে দ্বিতীয়টি বিন্দু মুক্ত শৈলী। "পয়েন্ট" শব্দটি একটি নামকৃত ফাংশন আর্গুমেন্টকে বোঝায় (এটি টপোলজি থেকে আসে) যা পরবর্তী ক্ষেত্রে অনুপস্থিত।

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

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

public static Function<A, C> compose(Function<A, B> first, Function<B, C> second) {
  return (value) -> second(first(value));
}

এখন ধরুন আমাদের একটি পদ্ধতি আছে Result deriveResult(Foo foo)। যেহেতু একটি ভবিষ্যদ্বাণী একটি ফাংশন, তাই আমরা একটি নতুন শিকারী তৈরি করতে পারি যা প্রথমে কল করে deriveResultএবং তারপরে প্রাপ্ত ফলাফলটি পরীক্ষা করে:

Predicate<Foo> isFoosResultInFuture =
    compose(this::deriveResult, this::isResultInFuture);

যদিও ল্যাম্বডাসের সাথে অর্থবহ শৈলীর ব্যবহার বাস্তবায়ন , সংজ্ঞায়িত করতে composeরচনাটির ব্যবহারisFoosResultInFuture বিন্দু মুক্ত কারণ কোনও যুক্তি উল্লেখ করার প্রয়োজন নেই।

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


1
আমি মনে করি আমার সহকর্মী এবং আমি যে বিষয়ে আলোচনা করছি তা হ'ল এই দুটি কার্যনির্বাহী: Predicate<Result> pred = this::isResultInFuture;এবং Predicate<Result> pred = resultInFuture();যেখানে রেজাল্ট ইনফিউচার () প্রত্যাবর্তন করে a Predicate<Result>। সুতরাং এটি যখন পদ্ধতি রেফারেন্স বনাম ল্যাম্বডা এক্সপ্রেশন কখন ব্যবহার করবেন তার দুর্দান্ত ব্যাখ্যা, এটি তৃতীয় ক্ষেত্রে পুরোপুরি আবরণ করে না।
এম জাস্টিন

4
@ এম জাস্টিন: resultInFuture();অ্যাসাইনমেন্টটি আমি উপস্থাপিত ল্যাম্বডা অ্যাসাইনমেন্টের সাথে একরকম, আমার ক্ষেত্রে আপনার resultInFuture()পদ্ধতিটি অন্তর্ভুক্ত না থাকলে। সুতরাং সেই পদ্ধতির দুটি দিকের দিকনির্দেশ রয়েছে (যার মধ্যে কোনও কিছুইই হয় না) - ল্যাম্বদা-নির্মাণ পদ্ধতি এবং ল্যাম্বদা নিজেই। আরো খারাপ!
জ্যাক

5

বর্তমানের উত্তরগুলির কোনওটিই আসলে প্রশ্নের মূলটিকে সম্বোধন করে না, যা ক্লাসের কোনও private boolean isResultInFuture(Result result)পদ্ধতি বা পদ্ধতি থাকা উচিত কিনা private Predicate<Result> resultInFuture()। যদিও তারা মূলত সমতুল্য, প্রত্যেকটির বিভিন্ন সুবিধা এবং অসুবিধা রয়েছে।

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

আপনি যদি এই পদ্ধতিটি বিভিন্ন কার্যকরী ইন্টারফেস বা ইন্টারফেসের বিভিন্ন ইউনিয়নের সাথে মানিয়ে নিতে চান তবে প্রথম পদ্ধতিরটিও উন্নত। উদাহরণস্বরূপ, আপনি হয়ত পদ্ধতিটির রেফারেন্সটি টাইপযুক্ত হতে চান Predicate<Result> & Serializable, যা দ্বিতীয় পদ্ধতি আপনাকে অর্জনের নমনীয়তা দেয় না।

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

শেষ পর্যন্ত সিদ্ধান্তটি সাপেক্ষিক। তবে আপনি যদি প্রিডিকেটে পদ্ধতিগুলি কল করতে না চান, তবে আমি প্রথম পদ্ধতির পক্ষে অগ্রাধিকার দিতে ঝুঁকছি।


প্রাকটিকের উপর উচ্চতর ক্রিয়াকলাপগুলি পদ্ধতি রেফারেন্সটি কাস্ট করে এখনও উপলব্ধ:((Predicate<Resutl>) this::isResultInFuture).whatever(...)
জ্যাক

4

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

হ্যাঁ, লোকেরা সার্বক্ষণিক টিউটোরিয়ালে ভেরিয়েবলগুলিতে ল্যাম্বডাস নির্ধারণ করে। এগুলি কেবলমাত্র তারা কীভাবে কাজ করে তার বিশদ বোঝার জন্য সহায়তার জন্য টিউটোরিয়াল। তুলনামূলকভাবে বিরল পরিস্থিতিতে (যেমন একটি এক্সপ্রেশন ব্যবহার করে দুটি ল্যাম্বডের মধ্যে পছন্দ করা পছন্দ করা) বাদে এগুলি বাস্তবে বাস্তবে সেভাবে ব্যবহার করা হয় না।

এর অর্থ যদি আপনার ল্যাম্বদা ইনলাইনের পরে সহজেই পঠনযোগ্য হওয়ার পক্ষে খুব কম হয়, যেমন @ জিমি জেমস'র মন্তব্যের উদাহরণের মতো, তবে এর জন্য যান:

people = sort(people, (a,b) -> b.age() - a.age());

পাঠযোগ্যতার সাথে এর তুলনা করুন যখন আপনি নিজের উদাহরণ ল্যাম্বদা ইনলাইন করার চেষ্টা করেন:

results.filter(result -> someOtherService.getResultDateFromDatabase(result).after(new Date()))...

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


2
পুনরায়: ভার্বোসটি - যদিও নতুন কার্যকরী সংযোজনগুলি নিজেরাই অনেক ভার্বোসিটি হ্রাস করে না, তারা এটি করার উপায়গুলিকে অনুমতি দেয়। উদাহরণস্বরূপ আপনি সংজ্ঞায়িত করতে পারেন: public static final <T> List<T> sort(List<T> list, Comparator<T> comparator)সুস্পষ্ট বাস্তবায়নের সাথে এবং তারপরে আপনি এই জাতীয় কোড লিখতে পারেন: people = sort(people, (a,b) -> b.age() - a.age());এটি একটি বড় উন্নতি আইএমও।
জিমি জেমস

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

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