কোনও প্রবাহ উপাদানকে সংশোধন করতে উঁকি দেওয়া () ব্যবহার করা কি অ্যান্টিপ্যাটার্ন?


36

মনে করুন আমার কাছে জিনিসগুলির একটি স্ট্রিম রয়েছে এবং আমি তাদের মাঝের স্ট্রিমটি "সমৃদ্ধ" করতে চাই, আমি এটি করতে এটি ব্যবহার peek()করতে পারি, যেমন:

streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

ধরে নিন যে কোডগুলিতে এই মুহুর্তে জিনিসগুলিকে রূপান্তর করা সঠিক আচরণ - উদাহরণস্বরূপ, thingMutatorপদ্ধতিটি "লাস্ট প্রসেসড" ক্ষেত্রটি বর্তমান সময়ের জন্য সেট করতে পারে।

তবে peek()বেশিরভাগ প্রসঙ্গে "চেহারা, তবে স্পর্শ করবেন না" এর অর্থ।

ব্যবহার করছে peek()থেকে পরিবর্তন ঘটান প্রবাহ উপাদান একটি antipattern বা অবিবেকী?

সম্পাদনা:

বিকল্প, আরও প্রচলিত, পদ্ধতি হ'ল গ্রাহককে রূপান্তর করা:

private void thingMutator(Thing thing) {
    thing.setLastProcessed(System.currentTimeMillis());
}

একটি ফাংশনে যা প্যারামিটারটি দেয়:

private Thing thingMutator(Thing thing) {
    thing.setLastProcessed(currentTimeMillis());
    return thing;
}

এবং map()পরিবর্তে ব্যবহার করুন:

stream.map(this::thingMutator)...

কিন্তু যে প্রবর্তন গতানুগতিক কোড ( return) এবং আমি বিশ্বাস করছি না এটা পরিষ্কার, কারণ আপনি জানেন peek()আয় একই বস্তু, কিন্তু map()এটা এমনকি এক নজরে এটি একই সময়ে পরিষ্কার নয় বর্গ বস্তুর।

আরও, আপনার সাথে peek()এমন একটি ল্যাম্বডা থাকতে পারে যা পরিবর্তিত হয় তবে map()আপনাকে ট্রেনের ধ্বংসস্তূপ তৈরি করতে হবে। তুলনা করা:

stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)

আমি মনে করি peek()সংস্করণটি আরও পরিষ্কার, এবং ল্যাম্বদা স্পষ্টভাবে পরিবর্তিত হচ্ছে, তাই কোনও "রহস্যময়" পার্শ্ব প্রতিক্রিয়া নেই। একইভাবে, যদি কোনও পদ্ধতির রেফারেন্স ব্যবহার করা হয় এবং পদ্ধতির নামটি স্পষ্টভাবে মিউটেশনকে বোঝায় তবে তাও স্পষ্ট এবং সুস্পষ্ট।

একটি ব্যক্তিগত নোটে, আমি peek()রূপান্তর করতে ব্যবহার করতে দ্বিধা করি না - আমি এটি খুব সুবিধাজনক বলে মনে করি।



1
আপনি কি peekএমন স্ট্রিম ব্যবহার করেছেন যা এর উপাদানগুলিকে গতিময়ভাবে উত্পন্ন করে? এটি এখনও কাজ করে না, বা পরিবর্তনগুলি কি হারিয়েছে? একটি স্রোতের উপাদানগুলিকে সংশোধন করা আমার কাছে বিশ্বাসযোগ্য নয়।
সেবাস্তিয়ান রেডল

@ সেবাস্তিয়ানআরডেল আমি এটি 100% নির্ভরযোগ্য পেয়েছি। প্রসেসিংয়ের সময় সংগ্রহ করার জন্য আমি এটি প্রথম ব্যবহার করেছি: List<Thing> list; things.stream().peek(list::add).forEach(...);খুব সহজ। ইদানীং. আমি এটা ব্যবহার করেছি প্রকাশ করার জন্য তথ্য যোগ করার জন্য: Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());। আমি জানি এই উদাহরণটি করার অন্যান্য উপায় আছে তবে আমি এখানে প্রচুর পরিমাণে সরল করছি। ব্যবহার peek()আরও কমপ্যাক্ট এবং আরও মার্জিত কোড আইএমএইচও উত্পাদন করে। পাঠযোগ্যতা একপাশে, এই প্রশ্নটি আপনি যা উত্থাপন করেছেন তা সম্পর্কে সত্যই; এটা কি নিরাপদ / নির্ভরযোগ্য?
বোহেমিয়ান


@Bohemian। আপনি কি এখনও এই পদ্ধতির সাথে অনুশীলন করছেন peek? আমার স্ট্যাকওভারফ্লোতে একই রকম প্রশ্ন রয়েছে এবং আশা করি আপনি এটি দেখতে এবং আপনার প্রতিক্রিয়া জানাতে পারেন। ধন্যবাদ। stackoverflow.com/questions/47356992/...
tsolakp

উত্তর:


23

আপনি সঠিক, শব্দের ইংরেজী অর্থে "উঁকি দেওয়া" এর অর্থ "চেহারা, তবে স্পর্শ করবেন না"।

তবে JavaDoc পদ বলে:

উঁকি

স্ট্রিম পিক (গ্রাহক পদক্ষেপ)

ফলাফলের স্ট্রিম থেকে উপাদানগুলি গ্রাস হওয়ায় প্রতিটি স্ট্রিমের সরবরাহিত ক্রিয়া সম্পাদন করে, এই স্ট্রিমের উপাদানগুলির সমন্বয়ে একটি স্ট্রিম ফিরিয়ে দেয়।

মূল শব্দ: "পারফর্মিং ... ক্রিয়া" এবং "গ্রাস"। জাভাডোকটি খুব স্পষ্ট যে আমাদের peekস্ট্রিমটি সংশোধন করার ক্ষমতা রাখার আশা করা উচিত ।

তবে জাভাডক আরও বলেছে:

এপিআই নোট:

এই পদ্ধতিটি মূলত ডিবাগিং সমর্থন করার জন্য উপস্থিত রয়েছে, যেখানে আপনি উপাদানগুলি পাইপলাইনের একটি নির্দিষ্ট বিন্দু পেরিয়ে যাওয়ার সময় দেখতে চান

এটি ইঙ্গিত করে যে এটি পর্যবেক্ষণের জন্য আরও লক্ষ্যযুক্ত eg

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

খুব কমপক্ষে, আমি এই লাইনগুলি বরাবর আপনার কোডটিতে একটি সংক্ষিপ্ত মন্তব্য যুক্ত করব:

// Note: this peek() call will modify the Things in the stream.
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

এই জাতীয় মন্তব্যের কার্যকারিতা সম্পর্কে মতামতগুলি পৃথক, তবে আমি এই ক্ষেত্রে এই জাতীয় মন্তব্য ব্যবহার করব।


1
যদি পদ্ধতির নামটি স্পষ্টভাবে কোনও রূপান্তর, উদাহরণস্বরূপ thingMutator, বা আরও কংক্রিটের ইঙ্গিত দেয় তবে আপনাকে কেন মন্তব্য করতে হবে? যদি আপনার resetLastProcessedবাধ্যতামূলক কারণ না থাকে তবে আপনার পরামর্শের মতো প্রয়োজনীয় মন্তব্যগুলি সাধারণত পরিবর্তনশীল এবং / অথবা পদ্ধতির নামের দুর্বল পছন্দগুলি নির্দেশ করে। যদি ভাল নামগুলি বেছে নেওয়া হয় তবে তা কি যথেষ্ট নয়? বা আপনি কি বলছেন যে একটি ভাল নাম থাকা সত্ত্বেও এর মধ্যে থাকা কোনও কিছু peek()অন্ধ স্পটের মতো যা বেশিরভাগ প্রোগ্রামাররা "স্কিম" (দৃষ্টিভঙ্গি এড়িয়ে) যাবেন? এছাড়াও, "প্রধানত ডিবাগিংয়ের জন্য" "কেবলমাত্র ডিবাগিংয়ের জন্য" একই জিনিস নয় - "মূলত" ব্যতীত অন্যান্য ব্যবহারের উদ্দেশ্য কী ছিল?
বোহেমিয়ান

@ বোহেমিয়ান আমি এটি ব্যাখ্যা করব মূলত কারণটির নামটি স্বজ্ঞাত নয়। এবং আমি ওরাকল এর পক্ষে কাজ করি না। আমি তাদের জাভা দলে নেই। তাদের উদ্দেশ্য কী তা আমার কোনও ধারণা নেই।

@ বোহেমিয়ান কারণ যখন উপাদানগুলি এমনভাবে পরিবর্তন করে তখন এটির জন্য যখন আমি পছন্দ করি না তখন আমি "উঁকি" তে লাইনটি পড়া বন্ধ করব কারণ "উঁকি" কোনও পরিবর্তন হয় না। কেবলমাত্র পরে যখন অন্য সমস্ত কোডের স্থানে আমি যে বাগটিটি অনুসরণ করছি তা না থাকায় আমি সম্ভবত এটি একটি ডিবাগারের সাহায্যে সজ্জিত করে আবার ফিরে আসব এবং সম্ভবত "ওএমজি, এই বিভ্রান্তিকর কোডটি এখানে কে রেখেছিল ?!"
ফ্র্যাঙ্ক হপকিন্স

@ বোহেমিয়ান এখানে উপস্থাপনের যেখানে সবকিছুই এক লাইনে রয়েছে, যে কোনওভাবেই পড়তে হবে, তবে "পিক" এর বিষয়বস্তু এড়িয়ে যেতে পারে এবং প্রায়শই একটি স্ট্রিম স্টেটমেন্ট প্রতি লাইনে একটি স্ট্রিম অপারেশন সহ একাধিক লাইনের উপরে চলে যায়
ফ্রাঙ্ক হপকিন্স

1

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


-2

এপিআই নোট আমাদের জানায় যে পদ্ধতিটি মূলত ডিবাগিং / লগিং / ফায়ারিং পরিসংখ্যান ইত্যাদির জন্য যুক্ত করা হয়েছে been

@apiNote এই পদ্ধতিটি মূলত ডিবাগিং সমর্থন করার জন্য বিদ্যমান, যেখানে আপনি * উপাদানগুলি পাইপলাইনে একটি নির্দিষ্ট বিন্দু পেরিয়ে যাওয়ার সময় দেখতে চান: *

       {@code
     * স্ট্রিম.ওফ ("এক", "দুই", "তিন", "চার")
     * ফিল্টার (ই -> ই দৈর্ঘ্য ()> 3)
     * .পেক (ই -> System.out.println ("ফিল্টারকৃত মান:" + ই))
     * .ম্যাপ (স্ট্রিং :: টু আপার কেস)
     * .পেক (ই -> System.out.println ("ম্যাপ করা মান:" + ই))
     * .collect (সংগ্রাহক। টু লিস্ট ());
     *


2
আপনার উত্তরটি ইতিমধ্যে স্নোম্যানের দ্বারা আবৃত।
ভিনসেন্ট সাভার্ড

3
এবং "প্রধানত" এর অর্থ "কেবল" নয়।
বোহেমিয়ান

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