বর্তমান অবস্থা
বর্তমান সেটআপটি ইন্টারফেস বিভাজন নীতি লঙ্ঘন করে (IOL in SOLID)।
উল্লেখ
উইকিপিডিয়া অনুসারে ইন্টারফেস পৃথককরণ নীতি (আইএসপি) বলেছে যে কোনও ক্লায়েন্টকে যে পদ্ধতিগুলি ব্যবহার না করে তার উপর নির্ভর করতে বাধ্য করা উচিত নয় । ইন্টারফেস পৃথককরণের নীতিটি ১৯৯০ এর দশকের মাঝামাঝি সময়ে রবার্ট মার্টিনই তৈরি করেছিলেন।
অন্য কথায়, এটি যদি আপনার ইন্টারফেস হয়:
public interface IUserBackend
{
User getUser(int uid);
User createUser(int uid);
void deleteUser(int uid);
void setPassword(int uid, string password);
}
তারপরে প্রতিটি শ্রেণি যা এই ইন্টারফেসটি প্রয়োগ করে তাদের অবশ্যই ইন্টারফেসের প্রতিটি তালিকাভুক্ত পদ্ধতিটি ব্যবহার করবে। বিকল্প নেই.
একটি সাধারণ পদ্ধতি আছে কিনা তা কল্পনা করুন:
public void HaveUserDeleted(IUserBackend backendService, User user)
{
backendService.deleteUser(user.Uid);
}
আপনি যদি বাস্তবে এটি তৈরি করে থাকেন যাতে কেবল প্রয়োগকারী কিছু শ্রেণিই একজন ব্যবহারকারীকে মুছতে সক্ষম হয়, তবে এই পদ্ধতিটি মাঝে মধ্যে আপনার মুখের মধ্যে ফুঁসে উঠবে (বা কিছুই করতে পারে না)। এটি ভাল নকশা নয়।
আপনার প্রস্তাবিত সমাধান
আমি একটি সমাধান দেখেছি যেখানে IUserInterface এর একটি প্রয়োগকৃত পদ্ধতি রয়েছে যা একটি পূর্ণসংখ্যা ফেরত দেয় যা অনুরোধকৃত ক্রিয়াকলাপগুলির সাথে বিটওয়াইড অ্যান্ডেড ক্রিয়াকলাপের বিটওয়াই ওআর ফলাফল।
আপনি মূলত যা করতে চান তা হ'ল:
public void HaveUserDeleted(IUserBackend backendService, User user)
{
if(backendService.canDeleteUser())
backendService.deleteUser(user.Uid);
}
প্রদত্ত শ্রেণি কোনও ব্যবহারকারীকে মুছতে সক্ষম কিনা তা আমরা ঠিক কীভাবে নির্ধারণ করি তা উপেক্ষা করছি । এটি কোনও বুলিয়ান, কিছুটা পতাকা, ... তাতে কিছু আসে যায় না। এটি সমস্ত বাইনারি উত্তরে ফোটে: এটি কোনও ব্যবহারকারীকে মুছতে পারে, হ্যাঁ বা না?
যে সমস্যাটি সমাধান করবে, তাই না? ভাল, প্রযুক্তিগতভাবে, এটি না। তবে এখন, আপনি লিসকভ সাবস্টিটিউশন নীতিমালা ( এসওএলআইডিতে এল) লঙ্ঘন করছেন ।
বরং জটিল উইকিপিডিয়া ব্যাখ্যার জন্য, আমি স্ট্যাকওভারফ্লোতে একটি শালীন উদাহরণ পেয়েছি । "খারাপ" উদাহরণটি নোট করুন:
void MakeDuckSwim(IDuck duck)
{
if (duck is ElectricDuck)
((ElectricDuck)duck).TurnOn();
duck.Swim();
}
আমি ধরে নিই যে আপনি এখানে মিল খুঁজে পেয়েছেন। এটি এমন একটি পদ্ধতি যা কোনও বিমূর্ত বস্তু ( IDuck
, IUserBackend
) হ্যান্ডেল করার কথা বলে মনে করা হয় , তবে আপোষযুক্ত শ্রেণির নকশার কারণে প্রথমে নির্দিষ্ট প্রয়োগগুলি হ্যান্ডেল করতে বাধ্য করা হয় ( ElectricDuck
এটি নিশ্চিত করুন যে এটি এমন কোনও IUserBackend
শ্রেণি নয় যা ব্যবহারকারীদের মুছে ফেলতে পারে না)।
এটি একটি বিমূর্ত পদ্ধতির বিকাশের উদ্দেশ্যকে পরাস্ত করে।
দ্রষ্টব্য: এখানে উদাহরণটি আপনার কেসের চেয়ে ঠিক করা আরও সহজ। উদাহরণস্বরূপ, এটি আছে যথেষ্ট ElectricDuck
বাঁকের নিজেই ভিতরেSwim()
পদ্ধতি। দুটি হাঁস এখনও সাঁতার কাটতে সক্ষম, তাই কার্যকরী ফলাফল একই।
আপনি অনুরূপ কিছু করতে চাইতে পারেন। না । আপনি কেবল কোনও ব্যবহারকারীকে মুছার ভান করতে পারবেন না তবে বাস্তবে একটি শূন্য পদ্ধতি রয়েছে। যদিও এটি প্রযুক্তিগত দৃষ্টিকোণ থেকে কাজ করে, আপনার বাস্তবায়নকারী শ্রেণি যখন কিছু করার কথা বলছে তখন আসলে কিছু করবে কিনা তা জানা অসম্ভব করে তোলে। এটি অবিবেচনাযোগ্য কোডের একটি প্রজনন ক্ষেত্র।
আমার প্রস্তাবিত সমাধান
তবে আপনি বলেছিলেন যে বাস্তবায়নকারী শ্রেণীর পক্ষে এই কয়েকটি পদ্ধতিতে কেবল পরিচালনা করা সম্ভব (এবং সঠিক)।
উদাহরণস্বরূপ, আসুন আমরা বলি যে এই পদ্ধতির প্রতিটি সম্ভাব্য সংমিশ্রণের জন্য, একটি শ্রেণি রয়েছে যা এটি বাস্তবায়ন করবে। এটি আমাদের সমস্ত ঘাঁটিগুলিকে coversেকে রেখেছে।
সমাধানটি এখানে ইন্টারফেসটি বিভক্ত করা ।
public interface IGetUserService
{
User getUser(int uid);
}
public interface ICreateUserService
{
User createUser(int uid);
}
public interface IDeleteUserService
{
void deleteUser(int uid);
}
public interface ISetPasswordService
{
void setPassword(int uid, string password);
}
নোট করুন যে আপনি আমার উত্তরের শুরুতে এটি দেখতে পেতেন। ইন্টারফেস বিচ্ছিন্নতার নীতি নামটি ইতিমধ্যেই জানায় যে এই নীতি আপনি করতে ডিজাইন করা হয়েছে ইন্টারফেসগুলি দলবদ্ধ করা অবশ্যই যথেষ্ট মাত্রায়।
এটি আপনাকে দয়া করে যেমন ইন্টারফেসগুলিকে মিক্স এবং মেলানোর অনুমতি দেয়:
public class UserRetrievalService
: IGetUserService, ICreateUserService
{
//getUser and createUser methods implemented here
}
public class UserDeleteService
: IDeleteUserService
{
//deleteUser method implemented here
}
public class DoesEverythingService
: IGetUserService, ICreateUserService, IDeleteUserService, ISetPasswordService
{
//All methods implemented here
}
প্রতিটি শ্রেণী তাদের ইন্টারফেসের চুক্তি ভঙ্গ না করে সিদ্ধান্ত নিতে পারে যে তারা কী করতে চায় do
এর অর্থ হ'ল আমাদের কোনও নির্দিষ্ট শ্রেণি কোনও ব্যবহারকারী মুছে ফেলতে সক্ষম কিনা তা খতিয়ে দেখার দরকার নেই। IDeleteUserService
ইন্টারফেস প্রয়োগ করে এমন প্রতিটি শ্রেণি একটি ব্যবহারকারীকে মুছতে সক্ষম হবে = লিসকভ সাবস্টিটিউশন নীতি লঙ্ঘন করবে না ।
public void HaveUserDeleted(IDeleteUserService backendService, User user)
{
backendService.deleteUser(user.Uid); //guaranteed to work
}
যদি কেউ বাস্তবায়িত না করে এমন কোনও বস্তু পাস করার চেষ্টা করে IDeleteUserService
তবে প্রোগ্রামটি সংকলন করতে অস্বীকার করবে। এ কারণেই আমরা টাইপ সুরক্ষা পছন্দ করি।
HaveUserDeleted(new DoesEverythingService()); // No problem.
HaveUserDeleted(new UserDeleteService()); // No problem.
HaveUserDeleted(new UserRetrievalService()); // COMPILE ERROR
পাদটীকা
আমি একটি চূড়ান্তভাবে উদাহরণটি গ্রহণ করেছি, ইন্টারফেসটিকে সবচেয়ে ছোট সম্ভাব্য অংশে আলাদা করে। তবে, যদি আপনার পরিস্থিতি আলাদা হয় তবে আপনি আরও বড় অংশ নিয়ে পালাতে পারেন।
উদাহরণস্বরূপ, ব্যবহারকারী তৈরি করতে পারে এমন প্রতিটি পরিষেবা যদি সর্বদা একজন ব্যবহারকারীকে মুছতে সক্ষম হয় (এবং তদ্বিপরীত), আপনি এই পদ্ধতিগুলি একটি একক ইন্টারফেসের অংশ হিসাবে রাখতে পারেন:
public interface IManageUserService
{
User createUser(int uid);
void deleteUser(int uid);
}
ছোট খণ্ডগুলিতে আলাদা হওয়ার পরিবর্তে এটি করার কোনও প্রযুক্তিগত সুবিধা নেই; তবে এটি উন্নয়ন কিছুটা সহজ করে তুলবে কারণ এর জন্য কম বয়লারপ্লাইটিংয়ের প্রয়োজন।
IUserBackend
থাকা উচিত নয়deleteUser
এ সব পদ্ধতি। এর অংশ হওয়া উচিতIUserDeleteBackend
(বা আপনি যেটিকে কল করতে চান)) ব্যবহারকারীদের মুছতে হবে এমনIUserDeleteBackend
কোডে যুক্তি থাকতে হবে , কোডটির প্রয়োজন নেই যে কার্যকারিতাটি ব্যবহার করবেIUserBackend
এবং অযৌক্তিক পদ্ধতিতে কোনও সমস্যা হবে না ।