CodeGym /وبلاگ جاوا /Random-FA /الگوی طراحی آداپتور چه مشکلاتی را حل می کند؟
John Squirrels
مرحله
San Francisco

الگوی طراحی آداپتور چه مشکلاتی را حل می کند؟

در گروه منتشر شد
توسعه نرم افزار توسط اجزای ناسازگاری که باید با هم کار کنند دشوارتر می شود. به عنوان مثال، اگر شما نیاز به ادغام یک کتابخانه جدید با یک پلتفرم قدیمی که در نسخه های قبلی جاوا نوشته شده است، ممکن است با اشیاء ناسازگار یا به عبارت بهتر رابط های ناسازگار مواجه شوید. الگوی طراحی آداپتور چه مشکلاتی را حل می کند؟  - 1در این صورت چه باید کرد؟ کد را دوباره بنویسیم؟ ما نمی توانیم این کار را انجام دهیم، زیرا تجزیه و تحلیل سیستم زمان زیادی می برد یا منطق داخلی برنامه نقض می شود. برای حل این مشکل، الگوی آداپتور ایجاد شد. به اشیاء با رابط های ناسازگار کمک می کند تا با هم کار کنند. بیایید ببینیم چگونه از آن استفاده کنیم!

بیشتر در مورد مشکل

ابتدا رفتار سیستم قدیمی را شبیه سازی می کنیم. فرض کنید بهانه هایی برای دیر رسیدن به محل کار یا مدرسه ایجاد می کند. Excuseبرای انجام این کار، یک رابط دارد که دارای generateExcuse()و متدها likeExcuse()است dislikeExcuse().

public interface Excuse {
   String generateExcuse();
   void likeExcuse(String excuse);
   void dislikeExcuse(String excuse);
}
کلاس WorkExcuseاین رابط را پیاده سازی می کند:

public class WorkExcuse implements Excuse {
   private String[] excuses = {"in an incredible confluence of circumstances, I ran out of hot water and had to wait until sunlight, focused using a magnifying glass, heated a mug of water so that I could wash.",
   "the artificial intelligence in my alarm clock failed me, waking me up an hour earlier than normal. Because it is winter, I thought it was still nighttime and I fell back asleep. Everything after that is a bit hazy.",
   "my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia."};
   private String [] apologies = {"This will not happen again, of course. I'm very sorry.", "I apologize for my unprofessional behavior.", "There is no excuse for my actions. I am not worthy of this position."};

   @Override
   public String generateExcuse() { // Randomly select an excuse from the array
       String result = "I was late today because " + excuses[(int) Math.round(Math.random() + 1)] + "\\n" +
               apologies[(int) Math.round(Math.random() + 1)];
       return result;
   }

   @Override
   public void likeExcuse(String excuse) {
       // Duplicate the element in the array so that its chances of being chosen are higher
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Remove the item from the array
   }
}
بیایید مثال خود را آزمایش کنیم:

Excuse excuse = new WorkExcuse();
System.out.println(excuse.generateExcuse());
خروجی:
"I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
I apologize for my unprofessional behavior.
حالا تصور کنید که یک سرویس بهانه آفرین راه اندازی کرده اید، آمار جمع آوری کرده اید و متوجه شده اید که بیشتر کاربران شما دانشجویان دانشگاه هستند. برای خدمت بهتر به این گروه، از یک توسعه دهنده دیگر خواستید سیستمی ایجاد کند که به طور خاص برای دانشجویان دانشگاه بهانه ایجاد کند. تیم توسعه تحقیقات بازار را انجام داد، بهانه‌ها را رتبه‌بندی کرد، هوش مصنوعی را به کار گرفت و این سرویس را با گزارش‌های ترافیک، گزارش‌های آب‌وهوا و غیره ادغام کرد. اکنون شما یک کتابخانه برای ایجاد بهانه برای دانشجویان دارید، اما رابط کاربری متفاوتی دارد: StudentExcuse.

public interface StudentExcuse {
   String generateExcuse();
   void dislikeExcuse(String excuse);
}
این رابط دو روش دارد: generateExcuse, که بهانه ایجاد می کند و dislikeExcuse, که از ظاهر شدن دوباره بهانه در آینده جلوگیری می کند. کتابخانه شخص ثالث قابل ویرایش نیست، یعنی نمی توانید کد منبع آن را تغییر دهید. چیزی که اکنون داریم یک سیستم با دو کلاس است که اینترفیس را پیاده سازی می کند Excuseو یک کتابخانه با SuperStudentExcuseکلاسی که StudentExcuseرابط را پیاده سازی می کند:

public class SuperStudentExcuse implements StudentExcuse {
   @Override
   public String generateExcuse() {
       // Logic for the new functionality
       return "An incredible excuse adapted to the current weather conditions, traffic jams, or delays in public transport schedules.";
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // Adds the reason to a blacklist
   }
}
کد را نمی توان تغییر داد. سلسله مراتب کلاس فعلی به این صورت است: الگوی طراحی آداپتور چه مشکلاتی را حل می کند؟  - 2این نسخه از سیستم فقط با رابط Excuse کار می کند. شما نمی توانید کد را بازنویسی کنید: در یک برنامه بزرگ، ایجاد چنین تغییراتی می تواند به یک فرآیند طولانی تبدیل شود یا منطق برنامه را از بین ببرد. ما می توانیم یک رابط پایه معرفی کنیم و سلسله مراتب را گسترش دهیم: الگوی طراحی آداپتور چه مشکلاتی را حل می کند؟  - 3برای انجام این کار، باید نام Excuseرابط را تغییر دهیم. اما سلسله مراتب اضافی در کاربردهای جدی نامطلوب است: معرفی یک عنصر ریشه مشترک معماری را می شکند. شما باید یک کلاس متوسط ​​را پیاده سازی کنید که به ما امکان می دهد از عملکرد جدید و قدیمی با حداقل ضرر استفاده کنیم. به طور خلاصه، شما به یک آداپتور نیاز دارید .

اصل پشت الگوی آداپتور

آداپتور یک شیء میانی است که امکان درک متد فراخوانی یک شی توسط شی دیگر را فراهم می کند. بیایید یک آداپتور برای مثال خود پیاده سازی کنیم و آن را فراخوانی کنیم Middleware. آداپتور ما باید رابطی را اجرا کند که با یکی از اشیاء سازگار باشد. بگذار باشد Excuse. این اجازه می دهد Middlewareتا متدهای شی اول را فراخوانی کنید. Middlewareتماس ها را دریافت می کند و آنها را به روشی سازگار به شی دوم ارسال می کند. Middlewareدر اینجا پیاده سازی با generateExcuseو متدها آمده است dislikeExcuse:

public class Middleware implements Excuse { // 1. Middleware becomes compatible with WorkExcuse objects via the Excuse interface

   private StudentExcuse superStudentExcuse;

   public Middleware(StudentExcuse excuse) { // 2. Get a reference to the object being adapted
       this.superStudentExcuse = excuse;
   }

   @Override
   public String generateExcuse() {
       return superStudentExcuse.generateExcuse(); // 3. The adapter implements an interface method
   }

    @Override
    public void dislikeExcuse(String excuse) {
        // The method first adds the excuse to the blacklist,
        // Then passes it to the dislikeExcuse method of the superStudentExcuse object.
    }
   // The likeExcuse method will appear later
}
تست (در کد مشتری):

public class Test {
   public static void main(String[] args) {
       Excuse excuse = new WorkExcuse(); // We create objects of the classes
       StudentExcuse newExcuse = new SuperStudentExcuse(); // that must be compatible.
       System.out.println("An ordinary excuse for an employee:");
       System.out.println(excuse.generateExcuse());
       System.out.println("\n");
       Excuse adaptedStudentExcuse = new Middleware(newExcuse); // Wrap the new functionality in the adapter object
       System.out.println("Using new functionality with the adapter:");
       System.out.println(adaptedStudentExcuse.generateExcuse()); // The adapter calls the adapted method
   }
}
خروجی:
An ordinary excuse for an employee:
I was late today because my pre-holiday mood slows metabolic processes in my body, leading to depression and insomnia.
There is no excuse for my actions. I am not worthy of this position. Using new functionality with the adapter:
بهانه ای باورنکردنی که با شرایط آب و هوایی فعلی، ترافیک یا تاخیر در برنامه های حمل و نقل عمومی سازگار است. این generateExcuseمتد به سادگی فراخوانی را به یک شی دیگر ارسال می کند، بدون اینکه هیچ تغییری اضافه شود. این dislikeExcuseروش مستلزم این بود که ابتدا بهانه را در لیست سیاه قرار دهیم. توانایی انجام پردازش داده های میانی دلیلی است که مردم الگوی آداپتور را دوست دارند. likeExcuseاما روش، که بخشی از اینترفیس است Excuseاما بخشی از رابط نیست ، چطور StudentExcuse؟ عملکرد جدید از این عملیات پشتیبانی نمی کند. UnsupportedOperationExceptionبرای این وضعیت اختراع شد . اگر عملیات درخواستی پشتیبانی نشود پرتاب می شود. از آن استفاده کنیم. پیاده سازی جدید کلاس به این صورت است Middleware:

public class Middleware implements Excuse {

   private StudentExcuse superStudentExcuse;

   public Middleware(StudentExcuse excuse) {
       this.superStudentExcuse = excuse;
   }

   @Override
   public String generateExcuse() {
       return superStudentExcuse.generateExcuse();
   }

   @Override
   public void likeExcuse(String excuse) {
       throw new UnsupportedOperationException("The likeExcuse method is not supported by the new functionality");
   }

   @Override
   public void dislikeExcuse(String excuse) {
       // The method accesses a database to fetch additional information,
       // and then passes it to the superStudentExcuse object's dislikeExcuse method.
   }
}
در نگاه اول، این راه حل چندان خوب به نظر نمی رسد، اما تقلید از عملکرد می تواند شرایط را پیچیده کند. اگر مشتری توجه کند و آداپتور به خوبی مستند باشد، چنین راه حلی قابل قبول است.

زمان استفاده از آداپتور

  1. زمانی که نیاز به استفاده از یک کلاس شخص ثالث دارید، اما رابط آن با برنامه اصلی ناسازگار است. مثال بالا نشان می دهد که چگونه می توان یک شی آداپتور ایجاد کرد که تماس ها را در قالبی قرار می دهد که یک شی هدف قابل درک باشد.

  2. هنگامی که چندین زیر کلاس موجود به برخی عملکردهای مشترک نیاز دارند. به جای ایجاد زیر کلاس های اضافی (که منجر به تکرار کد می شود)، بهتر است از یک آداپتور استفاده کنید.

مزایا و معایب

مزیت: آداپتور جزئیات پردازش درخواست ها را از یک شی به شی دیگر از مشتری پنهان می کند. کد سرویس گیرنده به قالب بندی داده ها یا مدیریت تماس ها به روش هدف فکر نمی کند. خیلی پیچیده است و برنامه نویسان تنبل هستند :) نقطه ضعف: پایه کد پروژه با کلاس های اضافی پیچیده است. اگر تعداد زیادی رابط های ناسازگار دارید، تعداد کلاس های اضافی می تواند غیرقابل مدیریت شود.

آداپتور را با نما یا دکوراتور اشتباه نگیرید

تنها با یک بازرسی سطحی، یک آداپتور می تواند با الگوهای نما و دکوراتور اشتباه گرفته شود. تفاوت بین آداپتور و نما در این است که نما یک رابط جدید را معرفی می کند و کل زیر سیستم را در بر می گیرد. و دکوراتور، بر خلاف آداپتور، خود شیء را به جای رابط تغییر می دهد.

الگوریتم گام به گام

  1. ابتدا مطمئن شوید که مشکلی دارید که این الگو می تواند آن را حل کند.

  2. رابط مشتری را تعریف کنید که برای تعامل غیر مستقیم با اشیاء ناسازگار استفاده می شود.

  3. کاری کنید که کلاس آداپتور رابط تعریف شده در مرحله قبل را به ارث ببرد.

  4. در کلاس adapter، یک فیلد برای ذخیره ارجاع به شیء adaptee ایجاد کنید. این مرجع به سازنده منتقل می شود.

  5. تمام روش های رابط مشتری را در آداپتور پیاده سازی کنید. یک روش ممکن است:

    • بدون ایجاد هیچ تغییری تماس ها را ارسال کنید

    • تغییر یا تکمیل داده ها، افزایش/کاهش تعداد تماس ها به روش هدف و غیره.

    • در موارد شدید، اگر یک روش خاص ناسازگار باقی بماند، یک UnsupportedOperationException پرتاب کنید. عملیات پشتیبانی نشده باید کاملاً مستند باشد.

  6. اگر برنامه فقط از کلاس آداپتور از طریق رابط مشتری استفاده کند (مانند مثال بالا)، آداپتور را می توان بدون دردسر در آینده گسترش داد.

البته، این الگوی طراحی نوشدارویی برای همه مشکلات نیست، اما می تواند به شما کمک کند مشکل ناسازگاری بین اشیاء با رابط های مختلف را به زیبایی حل کنید. توسعه‌دهنده‌ای که الگوهای اولیه را می‌داند، چندین قدم جلوتر از کسانی است که فقط می‌دانند چگونه الگوریتم بنویسند، زیرا الگوهای طراحی برای ایجاد برنامه‌های کاربردی جدی مورد نیاز هستند. استفاده مجدد از کد چندان دشوار نیست و نگهداری آن لذت بخش می شود. برای امروز کافی است! اما ما به زودی با الگوهای طراحی مختلف آشنا خواهیم شد :)
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION