কিছু সাধারণ কোডে কীভাবে রিফ্যাক্টর কোড করবেন?


16

পটভূমি

আমি একটি চলমান সি # প্রকল্পে কাজ করছি। আমি সি # প্রোগ্রামার নই, প্রাথমিকভাবে একজন সি ++ প্রোগ্রামার। সুতরাং আমাকে মূলত সহজ এবং পুনরায় নিয়ন্ত্রণের কাজ অর্পণ করা হয়েছিল।

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

তারা এখানে ঠিকঠাক করেছে কিনা তা নিয়ে আমি বিতর্ক করতে আসছি না। আমি যেমন রিফ্যাক্টর করছি, আমি ভাবছি যে আমার রিফ্যাক্টর কোডটি জটিল বলে মনে হচ্ছে আমি সঠিক উপায়ে এটি করছি কিনা! এখানে সহজ উদাহরণ হিসাবে আমার কাজ।

সমস্যা

ছয় শ্রেণীর আছে: A, B, C, D, Eএবং F। সমস্ত ক্লাসের একটি ফাংশন আছে ExecJob()। সমস্ত ছয়টি বাস্তবায়ন খুব একই রকম। মূলত, প্রথমে A::ExecJob()লেখা ছিল। তারপরে কিছুটা আলাদা সংস্করণ প্রয়োজন ছিল যা B::ExecJob()কপি-পেস্ট-সংশোধন করে প্রয়োগ করা হয়েছিল A::ExecJob()। যখন আরেকটি ভিন্ন সংস্করণ প্রয়োজন হয়, C::ExecJob()তখন লেখা ছিল এবং ইত্যাদি। সমস্ত ছয়টি বাস্তবায়নের কয়েকটি সাধারণ কোড রয়েছে, তারপরে কোডের কিছু আলাদা লাইন, তারপরে আবার কিছু সাধারণ কোড এবং এরকম। বাস্তবায়নগুলির একটি সাধারণ উদাহরণ এখানে:

A::ExecJob()
{
    S1;
    S2;
    S3;
    S4;
    S5;
}

B::ExecJob()
{
    S1;
    S3;
    S4;
    S5;
}

C::ExecJob()
{
    S1;
    S3;
    S4;
}

যেখানে SNঠিক একই বক্তব্যগুলির একটি গ্রুপ।

এগুলিকে সাধারণ করার জন্য, আমি অন্য একটি ক্লাস তৈরি করেছি এবং একটি ফাংশনে সাধারণ কোডটি সরিয়ে নিয়েছি। কোন গ্রুপের বিবৃতিগুলি কার্যকর করা উচিত তা নিয়ন্ত্রণ করতে প্যারামিটার ব্যবহার করে:

Base::CommonTask(param)
{
    S1;
    if (param.s2) S2;
    S3;
    S4;
    if (param.s5) S5;
}

A::ExecJob() // A inherits Base
{
    param.s2 = true;
    param.s5 = true;
    CommonTask(param);
}

B::ExecJob() // B inherits Base
{
    param.s2 = false;
    param.s5 = true;
    CommonTask(param);
}

C::ExecJob() // C inherits Base
{
    param.s2 = false;
    param.s5 = false;
    CommonTask(param);
}

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

যদিও সমস্ত বাস্তবায়নগুলি সাধারণ কোড ভাগ করে নিচ্ছে এবং ExecJob()ফাংশনগুলি কর্টর খুঁজছেন, সেখানে দুটি সমস্যা রয়েছে যা আমাকে বিরক্ত করছে:

  • যে কোনও পরিবর্তনের জন্য CommonTask(), সমস্ত ছয়টি (এবং ভবিষ্যতে আরও বেশি হতে পারে) বৈশিষ্ট্যগুলি পরীক্ষা করা দরকার।
  • CommonTask()ইতিমধ্যে জটিল। এটি সময়ের সাথে আরও জটিল হয়ে উঠবে।

আমি কি এটি সঠিক উপায়ে করছি?


মার্টিন ফাউলারের রিফ্যাক্টরিং বইতে রিফ্যাক্টরিং কোডের জন্য প্রচুর সুনির্দিষ্ট কৌশল রয়েছে যা আপনি দরকারী মনে করতে পারেন।
অ্যালান

উত্তর:


14

হ্যাঁ, আপনি একেবারে সঠিক পথে আছেন!

আমার অভিজ্ঞতায় আমি লক্ষ্য করেছি যে জিনিসগুলি যখন জটিল হয় তখন পরিবর্তনগুলি ছোট পদক্ষেপে ঘটে। আপনি যা করেছেন তা হ'ল বিবর্তন প্রক্রিয়াটির প্রথম ধাপ (বা রিফ্যাক্টরিং প্রক্রিয়া)। এখানে পদক্ষেপ 2 এবং পদক্ষেপ 3:

ধাপ ২

class Base {
  method ExecJob() {
    S1();
    S2();
    S3();
    S4();
    S5();
  }
  method S1() { //concrete implementation }
  method S3() { //concrete implementation }
  method S4() { //concrete implementation}
  abstract method S2();
  abstract method S5();
}

class A::Base {
  method S2() {//concrete implementation}
  method S5() {//concrete implementation}
}

class B::Base {
  method S2() { // empty implementation}
  method S5() {//concrete implementation}
}

class C::Base {
  method S2() { // empty implementation}
  method S5() { // empty implementation}
}

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

ধাপ 3

interface JobExecuter {
  void executeJob();
}
class A::JobExecuter {
  void executeJob(){
     helper = new Helper();
     helper->S1();
     helper->S2();
     helper->S3();
     helper->S4();
     helper->S5();
  }
}

class B::JobExecuter {
  void executeJob(){
     helper = new Helper();
     helper->S1();
     helper->S3();
     helper->S4();
     helper->S5();
  }
}

class C::JobExecuter {
  void executeJob(){
     helper = new Helper();
     helper->S1();
     helper->S3();
     helper->S4();
  }
}

class Base{
   void ExecJob(JobExecuter executer){
       executer->executeJob();
   }
}

class Helper{
    void S1(){//Implementation} 
    void S2(){//Implementation}
    void S3(){//Implementation}
    void S4(){//Implementation} 
    void S5(){//Implementation}
}

এটি 'স্ট্র্যাটেজি ডিজাইনের প্যাটার্ন' এবং এটি আপনার কেসের জন্য উপযুক্ত as কাজটি কার্যকর করার জন্য বিভিন্ন কৌশল রয়েছে এবং প্রতিটি শ্রেণি (এ, বি, সি) এটিকে আলাদাভাবে প্রয়োগ করে।

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


"পদক্ষেপ 2" এ বর্ণিত সমাধানের সাথে আমি যে বড় সমস্যাটি দেখছি তা হ'ল এস 5 এর কংক্রিট বাস্তবায়ন দু'বার উপস্থিত রয়েছে।
ব্যবহারকারী 281377

1
হ্যাঁ, কোড নকলটি মুছে ফেলা হয়নি! এবং এটি বিমূর্ততা কাজ করছে না এর আরেকটি সূচক। আমি প্রক্রিয়াটি সম্পর্কে কীভাবে ভাবছি তা দেখানোর জন্য আমি কেবল দ্বিতীয় ধাপটি বাইরে রাখতে চেয়েছিলাম; আরও ভাল কিছু আবিষ্কারের জন্য ধাপে ধাপে
গুভেন

1
+1 খুব ভাল কৌশল (এবং আমি প্যাটার্ন সম্পর্কে কথা বলছি না )!
জর্ডো

7

আপনি আসলে সঠিক জিনিস করছেন। আমি এটি বলছি কারণ:

  1. আপনার যদি কোনও সাধারণ টাস্ক কার্যকারিতার জন্য কোডটি পরিবর্তন করতে হয় তবে আপনার 6 টি ক্লাসে এটি পরিবর্তন করার দরকার নেই যা যদি আপনি একটি সাধারণ শ্রেণিতে না লিখেন তবে কোডটি ধারণ করে।
  2. কোডের লাইনের সংখ্যা হ্রাস পাবে।

3

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

উচ্চ স্তরের কোডটি ছোট ছোট পদ্ধতিগুলির একটি গুচ্ছ পুনরায় ব্যবহার করতে দিন, উচ্চ স্তরের কোডটি ভাগ করে নেওয়া বেসের বাইরে ছেড়ে দিন।

আপনার পাতায় / কংক্রিটের বাস্তবায়নে আপনার প্রচুর বয়লার প্লেট থাকবে। আতঙ্কিত হবেন না, ঠিক আছে। সমস্ত কোড সরাসরি, বুঝতে সহজ। স্টাফ বিরতিতে আপনাকে মাঝে মধ্যে এটিকে পুনরায় সাজিয়ে তুলতে হবে তবে এটি পরিবর্তন করা সহজ হবে।

আপনি উচ্চ স্তরের কোডে প্রচুর নিদর্শন দেখতে পাবেন। কখনও কখনও তারা বাস্তব হয়, বেশিরভাগ সময় তারা হয় না। উপরের পাঁচটি প্যারামিটারের "কনফিগারেশন" দেখতে একই রকম দেখতে পাওয়া যায়, তবে তা হয় না। এগুলি তিনটি সম্পূর্ণ ভিন্ন কৌশল।

এছাড়াও খেয়াল রাখতে চান যে আপনি রচনা দিয়ে এই সব করতে পারেন এবং উত্তরাধিকার সম্পর্কে কখনও চিন্তা করবেন না। আপনার কম দম্পতি হবে।


3

আমি আপনি হলে আমি শুরুতে সম্ভবত আরও 1 টি পদক্ষেপ যুক্ত করব: একটি ইউএমএল-ভিত্তিক গবেষণা study

কোডটি সমস্ত সাধারণ অংশ একত্রে একত্রিত করা সর্বদা সেরা পদক্ষেপ নয়, ভাল পদ্ধতির চেয়ে অস্থায়ী সমাধানের মতো বলে মনে হয়।

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

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


কোনও রিফ্যাক্টরিং করার আগে এটি প্রথম পদক্ষেপ হওয়া উচিত। কোডটি ম্যাপ করার জন্য যথেষ্ট পরিমাণে বোঝা না হওয়া অবধি (ইউএমএল বা ওয়াইল্ডসের কোনও অন্য মানচিত্র) রিফ্যাক্টরিং অন্ধকারে আর্কিটেকচারিং হবে।
Kzqai

3

প্রথম দিকটি, এটি যেখানে চলছে তা নির্বিশেষে, আপাতভাবে বড় পদ্ধতিটিকে A::ExecJobছোট ছোট টুকরো টুকরো করা উচিত।

সুতরাং, পরিবর্তে

A::ExecJob()
{
    S1; // many lines of code
    S2; // many lines of code
    S3; // many lines of code
    S4; // many lines of code
    S5; // many lines of code
}

তুমি পাও

A::ExecJob()
{
    S1();
    S2();
    S3();
    S4();
    S5();
}

A:S1()
{
   // many lines of code
}

A:S2()
{
   // many lines of code
}

A:S3()
{
   // many lines of code
}

A:S4()
{
   // many lines of code
}

A:S5()
{
   // many lines of code
}

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

B::ExecJob()
{
    S1();
    S3();
    S4();
    S5();
}

তবে কেন এত এত ক্লাস আছে? হতে পারে আপনি তাদের সকলকে একটি একক শ্রেণীর সাথে প্রতিস্থাপন করতে পারেন যার একটি নির্মাণকারী রয়েছে যা জানানো যেতে পারে কোন ক্রিয়াগুলি প্রয়োজনীয় ExecJob


2

আমি অন্যান্য উত্তরের সাথে একমত যে আপনার পদ্ধতির সঠিক লাইন বরাবর রয়েছে যদিও আমি মনে করি না যে উত্তরাধিকারটি সাধারণ কোডটি প্রয়োগের সর্বোত্তম উপায় - আমি রচনা পছন্দ করি। সি ++ এফএকিউ থেকে যা এটি আমার চেয়ে আরও বেশি ভাল ব্যাখ্যা করতে পারে: http://www.parashift.com/c++-faq/priv-inherit-vs-compos.html


1

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

আসুন ধরে নেওয়া যাক আপনি স্থির করে নিন যে আপনার ক্ষেত্রে সাধারণ বেস শ্রেণিটি সঠিক জিনিস। তারপর দ্বিতীয় জিনিস আমি করতে হবে নিশ্চিত করুন যে আপনার কোড টুকরা S1 এস 5 প্রতিটি আলাদা পদ্ধতিতে প্রয়োগ করা হয় করা হয় S1()থেকে S5()আপনার বেস ক্লাসের। এরপরে "এক্সিকিউজব" ফাংশনগুলির মতো দেখতে হবে:

A::ExecJob()
{
    S1();
    S2();
    S3();
    S4();
    S5();
}

B::ExecJob()
{
    S1();
    S3();
    S4();
    S5();
}

C::ExecJob()
{
    S1();
    S3();
    S4();
}

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

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

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

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