রেফারেন্স দ্বারা পাস করা কোনও বস্তুর সংশোধন করা কি একটি খারাপ অভ্যাস?


12

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

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

পুরানো পদ্ধতি:

public void InsertUser(User user) {
    user.Username = GenerateUsername(user);
    user.Password = GeneratePassword(user);

    context.Users.Add(user);
}

নতুন পদ্ধতি:

public void InsertUser(User user) {
    SetUsername(user);
    SetPassword(user);

    context.Users.Add(user);
}

private void SetUsername(User user) {
    var username = "random business logic";

    user.Username = username;
}

private void SetPassword(User user) {
    var password = "more business logic";

    user.Password = password;
}

মূলত, অন্য পদ্ধতি থেকে কোনও সম্পত্তির মূল্য নির্ধারণের অভ্যাসটি কি খারাপ অভ্যাস?


6
ঠিক তাই বলা হয়েছে ... আপনি এখানে রেফারেন্স দিয়ে কিছু পাস করেন নি। মান দ্বারা রেফারেন্স পাস করা মোটেও একই জিনিস নয়।
সিএইচও

4
@ সিএইচও: এটি কোনও পার্থক্য ছাড়াই পার্থক্য। কোডের আচরণ একই রকম হয় less
রবার্ট হার্ভে

2
@ জেডিডিভিস: মৌলিকভাবে, আপনার দুটি উদাহরণের মধ্যে একমাত্র আসল পার্থক্য হ'ল আপনি সেট ব্যবহারকারীর নাম এবং পাসওয়ার্ড ক্রিয়াকলাপগুলিকে একটি অর্থবোধক নাম দিয়েছেন।
রবার্ট হার্ভে

1
আপনার সমস্ত কল এখানে রেফারেন্স দ্বারা হয়। মান দ্বারা পাস করতে আপনাকে সি # তে একটি মান টাইপ ব্যবহার করতে হবে।
ফ্র্যাঙ্ক হিলেমান

2
@ ফ্র্যাঙ্কহিল্যান: সমস্ত কল এখানে মান অনুসারে। এটি সি # এ ডিফল্ট। পাসিং একটি রেফারেন্স এবং ক্ষণস্থায়ী দ্বারা রেফারেন্স বিভিন্ন পশুরা হয়, এবং পার্থক্য ব্যাপার। যদি userরেফারেন্স দিয়ে পাস করা হয়, কোডটি কলারের হাত থেকে এটি কুঁচকে যেতে পারে এবং কেবল, বলুন, বলার মাধ্যমে এটি প্রতিস্থাপন করতে পারে user = null;
সিএইচও

উত্তর:


10

এখানে সমস্যাটি হ'ল একটি Userআসলে দুটি ভিন্ন জিনিস থাকতে পারে:

  1. একটি সম্পূর্ণ ব্যবহারকারীর সত্তা, যা আপনার ডেটা স্টোরে যেতে পারে।

  2. ব্যবহারকারী সত্তা তৈরির প্রক্রিয়া শুরু করার জন্য কলারের কাছ থেকে প্রয়োজনীয় ডেটা উপাদানগুলির সেট। উপরের # 1 এর মতো সত্যিকারের বৈধ ব্যবহারকারী হওয়ার আগে সিস্টেমটিকে অবশ্যই একটি ব্যবহারকারীর নাম এবং পাসওয়ার্ড যুক্ত করতে হবে।

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

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

public User InsertUser(EnrollRequest request) {
    var userName = GenerateUserName();
    var password = GeneratePassword();

    //You might want to replace this with a factory call, but "new" works here as an example
    var newUser = new User
    (
        request.Name, 
        request.Email, 
        userName, 
        password
    );
    context.Users.Add(user);
    return newUser;
}

কলারটি কেবল তালিকাভুক্তির তথ্য দিয়ে শুরু হয় এবং এটি isোকানোর পরে সম্পূর্ণ ব্যবহারকারীকে ফিরে পায়। এইভাবে আপনি যে কোনও ক্লাসকে পরিবর্তন করতে পারবেন না এবং inোকানো একজন ব্যবহারকারী এবং যারা নেই তার মধ্যেও আপনার টাইপ-নিরাপদ পার্থক্য রয়েছে।


2
নামের মতো একটি পদ্ধতির InsertUserসন্নিবেশের পার্শ্ব প্রতিক্রিয়া হতে পারে। এই প্রসঙ্গে "আইকি" এর অর্থ কী তা আমি নিশ্চিত নই। যোগ করা হয়নি এমন একটি "ব্যবহারকারী" ব্যবহার করার ক্ষেত্রে, আপনি মনে করছেন যে বিন্দুটি মিস করেছেন। যদি এটি যুক্ত না করা হয়, তবে এটি কোনও ব্যবহারকারী নয়, কেবল একটি ব্যবহারকারী তৈরি করার অনুরোধ। আপনি অবশ্যই অনুরোধটি নিয়ে কাজ করতে মুক্ত হন।
জন উ

@ ক্যান্ডিওরেঞ্জ এই পদ্ধতিটি কল করার পছন্দসই প্রভাবগুলি পার্শ্ব প্রতিক্রিয়া নয়। আপনি অবিলম্বে যুক্ত করতে না চাইলে আপনি অন্য একটি পদ্ধতি তৈরি করেন যা ব্যবহারের ক্ষেত্রে সন্তুষ্ট হয়। তবে এটি প্রশ্নে পাস হওয়া ব্যবহারের মামলা নয় তাই বলা উচিত উদ্বেগ গুরুত্বপূর্ণ নয়।
অ্যান্ডি

@ ক্যান্ডিওরেঞ্জ যদি কাপলিং ক্লায়েন্ট কোডটি আরও সহজ করে দেয় আমি বলব যে এটি ভাল। ডেভস বা পোস্ট ভবিষ্যতে অপ্রাসঙ্গিক বলে মনে করতে পারে। যদি সেই সময়টি আসে, আপনি কোডটি পরিবর্তন করুন। এমন কোনও কোড তৈরির চেষ্টা করার চেয়ে অপব্যয় আর কোনও সম্ভাব্য ভবিষ্যতের ব্যবহারের কেস পরিচালনা করতে পারে।
অ্যান্ডি

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

5

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

 repo.InsertUser(user);

ভান্ডারগুলি অভ্যন্তরীণ স্থিতি পরিবর্তন করতে, ব্যবহারকারীর অবজেক্টের অবস্থা নয়। এই সমস্যাটি আপনার উভয় বাস্তবায়নে বিদ্যমান রয়েছে , কীভাবে InsertUserঅভ্যন্তরীণভাবে এটি সম্পূর্ণ অপ্রাসঙ্গিক।

এটি সমাধান করার জন্য, আপনি হয়

  • সন্নিবেশ থেকে পৃথক সূচনা (যাতে কলকারীকে InsertUserএকটি সম্পূর্ণ আরম্ভীকৃত Userবস্তু সরবরাহ করা প্রয়োজন, বা,

  • ব্যবহারকারীর অবজেক্টের নির্মাণ প্রক্রিয়াতে সূচনাটি তৈরি করুন (অন্যান্য উত্তরগুলির কয়েকটি হিসাবে প্রস্তাবিত), বা

  • কেবল পদ্ধতির জন্য আরও ভাল নাম সন্ধান করার চেষ্টা করুন যা এটি কী করে তা আরও স্পষ্টভাবে প্রকাশ করে।

তাই মত একটি পদ্ধতি নাম চয়ন করুন PrepareAndInsertUser, OrchestrateUserInsertion, InsertUserWithNewNameAndPasswordবা যাই হোক না কেন আপনি পার্শ্ব প্রতিক্রিয়া আরো স্বচ্ছ করে তুলতে পছন্দ করে।

অবশ্যই, এত দীর্ঘ পদ্ধতির নামটি ইঙ্গিত করে যে পদ্ধতিটি সম্ভবত "অত্যধিক" (এসআরপি লঙ্ঘন) করছে, তবে কখনও কখনও আপনি এটি চান না বা সহজেই এটি ঠিক করতে পারবেন না, এবং এটি হ'ল স্বল্পতম হস্তক্ষেপমূলক, বাস্তববাদী সমাধান।


3

আপনি যে দুটি অপশন বেছে নিয়েছেন আমি তার দিকে নজর দিচ্ছি এবং আমাকে বলতে হবে যে আমি প্রস্তাবিত নতুন পদ্ধতির চেয়ে পুরানো পদ্ধতিটিকে বেশি পছন্দ করি। মৌলিকভাবে একই জিনিসটি করা সত্ত্বেও এর কয়েকটি কারণ রয়েছে।

উভয় ক্ষেত্রেই আপনি সেটিংস স্থাপন করছেন user.UserNameএবং user.Password। পাসওয়ার্ড আইটেমটি সম্পর্কে আমার কাছে সংরক্ষণ রয়েছে তবে সেই রিজার্ভেশনগুলি বিষয়টিতে জার্মানী নয়।

রেফারেন্স অবজেক্টগুলিকে সংশোধন করার প্রভাব

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

পুরানো পদ্ধতি বনাম নতুন পদ্ধতি

পুরানো পদ্ধতি জিনিসগুলি পরীক্ষা করা সহজ করে তুলেছিল:

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

নতুন পদ্ধতিটি রূপান্তরগুলি আড়াল করে:

  • আপনি জানেন না যে Userআপনি 2 স্তর গভীর পর্যন্ত না যাওয়া পর্যন্ত অবজেক্টটি পরিবর্তন হচ্ছে
  • এই Userক্ষেত্রে বস্তুর পরিবর্তনগুলি আরও আশ্চর্যজনক ing
  • SetUserName()ব্যবহারকারীর নাম সেট করার চেয়েও বেশি কিছু করে। বিজ্ঞাপনে এটি সত্য নয় যা নতুন বিকাশকারীদের পক্ষে কীভাবে আপনার অ্যাপ্লিকেশনে জিনিসগুলি কাজ করে তা আবিষ্কার করা আরও শক্ত করে তোলে

1

জন উ এর উত্তরটির সাথে আমি পুরোপুরি একমত। তাঁর পরামর্শটি ভাল। তবে এটি আপনার সরাসরি প্রশ্নটিকে কিছুটা বাদ দেয়।

মূলত, অন্য পদ্ধতি থেকে কোনও সম্পত্তির মূল্য নির্ধারণের অভ্যাসটি কি খারাপ অভ্যাস?

সহজাতভাবে নয় Not

আপনি এটিকে খুব বেশি দূরে নিতে পারবেন না, কারণ আপনি অপ্রত্যাশিত আচরণে চলে যাবেন। উদাহরণস্বরূপ PrintName(myPerson)ব্যক্তির বস্তুটি পরিবর্তন করা উচিত নয়, যেহেতু পদ্ধতিটি বোঝায় যে এটি কেবল বিদ্যমান মানগুলি পড়তে আগ্রহী । তবে এটি আপনার মামলার চেয়ে আলাদা যুক্তি, যেহেতু SetUsername(user)দৃ strongly়তার সাথে বোঝায় যে এটি মান নির্ধারণ করতে চলেছে।


এটি প্রকৃতপক্ষে আমি ইউনিট / ইন্টিগ্রেশন টেস্টগুলির জন্য প্রায়শই ব্যবহার করি, যেখানে আমি নির্দিষ্টভাবে একটি আইটেমের পরিবর্তন করতে একটি পদ্ধতি তৈরি করি যাতে যে নির্দিষ্ট পরিস্থিতিতে আমি পরীক্ষা করতে চাই।

উদাহরণ স্বরূপ:

var myContract = CreateEmptyContract();

ArrrangeContractDeletedStatus(myContract);

আমি স্পষ্টভাবে প্রত্যাশা করি যে ArrrangeContractDeletedStatusপদ্ধতিটি myContractঅবজেক্টের অবস্থা পরিবর্তন করবে ।

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

আমি যদি একত্রীকরণ CreateEmptyContractএবং ArrrangeContractDeletedStatusএকটি একক পদ্ধতিতে; আমি মুছে ফেলা অবস্থায় পরীক্ষা করতে চাই এমন প্রতিটি পৃথক চুক্তির জন্য আমাকে এই পদ্ধতির একাধিক রূপ তৈরি করতে হবে।

এবং যখন আমি কিছু করতে পারি:

myContract = ArrrangeContractDeletedStatus(myContract);

এটি হয় অপ্রয়োজনীয় (যেহেতু আমি যাইহোক বস্তুটি পরিবর্তন করছি), বা আমি এখন নিজেকে myContractঅবজেক্টের গভীর ক্লোন তৈরি করতে বাধ্য করছি ; যা আপনি প্রতিটি ক্ষেত্রেই কভার করতে চান তা অত্যধিক কঠিন (কল্পনা করুন যে আমি যদি বিভিন্ন স্তরের 'ন্যাভিগেশনাল সম্পত্তি চাই তবে আমার কি সবগুলি ক্লোন করা দরকার? কেবলমাত্র শীর্ষ স্তরের সত্তা? ... এতগুলি প্রশ্ন, এতগুলি অন্তর্নিহিত প্রত্যাশা )

নীতি বিষয় হিসাবে বস্তুটি পরিবর্তন না করে কেবল আরও কাজ না করেই আমি যা চাই তা পাওয়ার সহজ উপায় হ'ল অবজেক্টটি পরিবর্তন করা।


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


0

আমি পুরানো পাশাপাশি নতুন সমস্যাযুক্ত খুঁজে পাই।

পুরানো উপায়:

1) InsertUser(User user)

এই আইডিটির নামটি যখন আমার আইডিইতে পপিং করে পড়ছে তখন আমার মনে প্রথম জিনিসটি আসে

ব্যবহারকারী কোথায় ?োকানো হয়?

পদ্ধতির নামটি পড়া উচিত AddUserToContext। কমপক্ষে, এটি হল, পদ্ধতিটি শেষ পর্যন্ত কী করে।

2) InsertUser(User user)

এটি পরিষ্কারভাবে অন্তত বিস্ময়ের নীতি লঙ্ঘন করে। বলুন, আমি এখনও অবধি আমার কাজটি করেছি এবং একটির নতুন ঘটনা তৈরি করেছি Userএবং তাকে একটি দিয়েছি nameএবং সেট করে passwordদিয়েছি, আমি অবাক হয়ে যাব:

ক) এটি কেবল কোনও কিছুতে কোনও ব্যবহারকারীকে সন্নিবেশ করে না

খ) এটি ব্যবহারকারীকে যেমন সন্নিবেশ করানোর আমার উদ্দেশ্যকেও বিকৃত করে; নাম এবং পাসওয়ার্ড ওভাররাইড করা হয়েছিল।

গ) এটি কি এটি নির্দেশ করে যে এটি একক দায়িত্বের নীতি লঙ্ঘন।

নতুন উপায়:

1) InsertUser(User user)

তবুও এসআরপি লঙ্ঘন এবং নূন্যতম বিস্ময়ের নীতি

2) SetUsername(User user)

প্রশ্ন

ব্যবহারকারীর নাম সেট করবেন? কি?

আরও ভাল: SetRandomNameবা এমন কিছু যা অভিপ্রায় প্রতিফলিত করে।

3) SetPassword(User user)

প্রশ্ন

পাসওয়ার্ড সেট করবেন? কি?

আরও ভাল: SetRandomPasswordবা এমন কিছু যা অভিপ্রায় প্রতিফলিত করে।


প্রস্তাবনা:

আমি যা পড়তে পছন্দ করব তা হ'ল কিছুটা:

public User GenerateRandomUser(UserFactory Uf){
    User u = Uf.GetUser();
    u.Name = GenerateRandomUserName();
    u.Password = GenerateRandomUserPassword();
    return u;
}

...

public void AddUserToContext(User u){
    this.context.Users.Add(u);
}

আপনার প্রাথমিক প্রশ্ন সম্পর্কে:

রেফারেন্স দ্বারা পাস করা কোনও বস্তুর সংশোধন করা কি একটি খারাপ অভ্যাস?

না, এটি আরও স্বাদের বিষয়।

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