JavaRush /مدونة جافا /Random-AR /محول نمط التصميم

محول نمط التصميم

نشرت في المجموعة
مرحبًا! اليوم سوف نتطرق إلى موضوع جديد مهم - الأنماط، أو بمعنى آخر - أنماط التصميم . ما هي الأنماط؟ أعتقد أنك تعرف عبارة "لا تعيد اختراع العجلة". في البرمجة، كما هو الحال في العديد من المجالات الأخرى، هناك عدد كبير من المواقف النموذجية. لكل واحد منهم، في عملية تطوير البرمجة، تم إنشاء حلول عمل جاهزة. هذه هي أنماط التصميم. نسبيًا، النمط هو مثال معين يقدم حلاً لموقف مثل: "إذا كان برنامجك يحتاج إلى القيام بشيء ما، فما هي أفضل طريقة للقيام بذلك". هناك الكثير من الأنماط، وقد تم تخصيص كتاب ممتاز بعنوان "دراسة أنماط التصميم" لها، والذي يجب عليك قراءته بالتأكيد. نمط التصميم "محول" - 2باختصار قدر الإمكان، يتكون النمط من مشكلة شائعة وحلها، والذي يمكن بالفعل اعتباره نوعًا من المعايير. وفي محاضرة اليوم سوف نتعرف على أحد هذه الأنماط والذي يسمى "المحول". اسمها معبر، وقد صادفت محولات في الحياة الواقعية أكثر من مرة. أحد المحولات الأكثر شيوعًا هو قارئات البطاقات، وهي مجهزة بالعديد من أجهزة الكمبيوتر وأجهزة الكمبيوتر المحمولة. نمط التصميم "محول" - 3تخيل أن لدينا نوعًا من بطاقة الذاكرة. ما المشكلة؟ الحقيقة هي أنها لا تعرف كيفية التفاعل مع الكمبيوتر. ليس لديهم واجهة مشتركة. يحتوي الكمبيوتر على موصل USB، لكن لا يمكنك إدخال بطاقة ذاكرة فيه. لا يمكن إدخال البطاقة في الكمبيوتر، ولهذا السبب لن نتمكن من حفظ الصور ومقاطع الفيديو والبيانات الأخرى. قارئ البطاقة هو محول يحل هذه المشكلة. بعد كل شيء، لديه كابل USB! على عكس البطاقة نفسها، يمكن إدخال قارئ البطاقة في الكمبيوتر. لديهم واجهة مشتركة مع الكمبيوتر - USB. دعونا نرى كيف سيبدو مع مثال:
public interface USB {

   void connectWithUsbCable();
}
هذه هي واجهة USB الخاصة بنا والطريقة الوحيدة هي إدخال كابل USB:
public class MemoryCard {

   public void insert() {
       System.out.println("Карта памяти успешно вставлена!");
   }

   public void copyData() {
       System.out.println("Данные скопированы на компьютер!");
   }
}
هذا هو فصلنا الذي ينفذ خريطة الذاكرة. إنه يحتوي بالفعل على طريقتين نحتاجهما، ولكن هنا تكمن المشكلة: فهو لا يطبق واجهة USB. لا يمكن إدخال البطاقة في فتحة USB.
public class CardReader implements USB {

   private MemoryCard memoryCard;

   public CardReader(MemoryCard memoryCard) {
       this.memoryCard = memoryCard;
   }

   @Override
   public void connectWithUsbCable() {
       this.memoryCard.insert();
       this.memoryCard.copyData();
   }
}
وهنا محولنا! ماذا يفعل الفصلCardReader ولماذا هو محول؟ انه سهل. تصبح الفئة التي يتم تكييفها (خريطة الذاكرة) أحد حقول المحول. وهذا أمر منطقي، لأننا في الحياة الواقعية نقوم أيضًا بإدخال بطاقة داخل قارئ البطاقة، وتصبح أيضًا جزءًا منها. على عكس بطاقة الذاكرة، يحتوي المحول على واجهة مشتركة مع الكمبيوتر. يحتوي على كابل USB، مما يعني أنه يمكنه الاتصال بأجهزة أخرى عبر USB. لذلك، في البرنامج، يقوم فصلنا CardReaderبتنفيذ واجهة USB. ولكن ماذا يحدث داخل هذه الطريقة؟ ويحدث بالضبط ما نحتاجه! يقوم المحول بتفويض العمل إلى بطاقة الذاكرة الخاصة بنا. بعد كل شيء، المحول نفسه لا يفعل شيئا، ليس لدى قارئ البطاقة أي وظيفة مستقلة. وظيفتها فقط ربط الكمبيوتر وبطاقة الذاكرة لتتمكن البطاقة من القيام بعملها ونسخ الملفات! يسمح المحول الخاص بنا بالقيام بذلك من خلال توفير واجهة (طريقة connectWithUsbCable()) خاصة به لتلبية "احتياجات" بطاقة الذاكرة. لنقم بإنشاء نوع من برنامج العميل الذي يحاكي الشخص الذي يريد نسخ البيانات من بطاقة الذاكرة:
public class Main {

   public static void main(String[] args) {

       USB cardReader = new CardReader(new MemoryCard());
       cardReader.connectWithUsbCable();

   }
}
ماذا حصلنا نتيجة لذلك؟ إخراج وحدة التحكم:
Карта памяти успешно вставлена!
Данные скопированы на компьютер!
عظيم، لقد اكتملت مهمتنا بنجاح! فيما يلي بعض الروابط الإضافية التي تحتوي على معلومات حول نمط المحول:

دروس مجردة القارئ والكاتب

سنعود الآن إلى هوايتنا المفضلة: سنتعلم فصلين جديدين للعمل مع الإدخال والإخراج :) كم تعلمنا منهم بالفعل، أتساءل؟ اليوم سنتحدث عن الفصول Readerو Writer. لماذا عنهم؟ لأن هذا سيكون مرتبطًا بقسمنا السابق - المحولات. دعونا ننظر إليهم بمزيد من التفصيل. لنبدأ بـ Reader"أ". Readerهي فئة مجردة، لذلك لن نتمكن من إنشاء كائناتها بشكل صريح. ولكن في الواقع، أنت تعرفه بالفعل! بعد كل شيء، الطبقات التي تعرفها جيدا BufferedReaderهي InputStreamReaderورثتها :)
public class BufferedReader extends Reader {}

public class InputStreamReader extends Reader {}
لذا، فإن الفصل InputStreamReaderهو محول كلاسيكي . كما تتذكر على الأرجح، يمكننا تمرير كائن إلى مُنشئه InputStream. في أغلب الأحيان نستخدم متغيرًا لهذا System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
ماذا تعمل، أو ماذا تفعل InputStreamReader؟ مثل أي محول، فإنه يحول واجهة إلى أخرى. في هذه الحالة، الواجهة InputStream"أ" إلى الواجهة Reader"أ". في البداية كان لدينا فئة InputStream. إنه يعمل بشكل جيد، لكنه يمكنه قراءة البايتات الفردية فقط. وبالإضافة إلى ذلك، لدينا فئة مجردة Reader. لديه وظائف ممتازة نحتاجها حقًا - يمكنه قراءة الأحرف! ونحن، بطبيعة الحال، في حاجة ماسة إلى هذه الفرصة. ولكن هنا نواجه مشكلة كلاسيكية تحلها المحولات عادةً - عدم توافق الواجهة. كيف يعبر عن نفسه؟ دعونا ننظر مباشرة إلى وثائق أوراكل. وهنا أساليب الطبقة InputStream. Паттерн проектирования «Адаптер» - 4مجموعة من الأساليب هي واجهة. كما ترون، read()هذه الفئة لديها طريقة (حتى في العديد من الإصدارات)، ولكن يمكنها قراءة البايتات فقط: إما بايتات فردية، أو عدة بايتات باستخدام المخزن المؤقت. هذا الخيار لا يناسبنا - نريد قراءة الشخصيات. الوظيفة التي نحتاجها تم تنفيذها بالفعل في فئة مجردةReader . ويمكن رؤية هذا أيضًا في الوثائق. Паттерн проектирования «Адаптер» - 5ومع ذلك، فإن الواجهات InputStream"a" و Reader"a" غير متوافقة! كما ترون، في جميع تطبيقات الطريقة، read()تختلف كل من المعلمات التي تم تمريرها وقيم الإرجاع. وهذا هو المكان الذي نحتاجه InputStreamReader! سيكون بمثابة محول بين فصولنا. كما هو الحال في مثال قارئ البطاقة، الذي نظرنا إليه أعلاه، نقوم بتمرير كائن الفئة "المعدلة" "داخليًا"، أي إلى مُنشئ فئة المحول. في المثال السابق، قمنا بتمرير كائن MemoryCardداخل CardReader. الآن نقوم بتمرير الكائن InputStreamإلى المنشئ InputStreamReader! كنوعية InputStreamنستخدم المتغير المألوف بالفعل System.in:
public static void main(String[] args) {

   InputStreamReader inputStreamReader = new InputStreamReader(System.in);
}
وبالفعل: من خلال النظر إلى الوثائق InputStreamReaderسنرى أن "التكيف" كان ناجحًا :) الآن لدينا طرق تسمح لنا بقراءة الأحرف. Паттерн проектирования «Адаптер» - 6وعلى الرغم من أن كائننا في البداية System.in(خيط مرتبط بلوحة المفاتيح) لم يسمح بذلك، إلا أن منشئي اللغة قاموا بحل هذه المشكلة عن طريق إنشاء نمط المحول . الطبقة المجردة Reader، مثل معظم فئات الإدخال/الإخراج، لها شقيق توأم - Writer. يتمتع بنفس الميزة الكبيرة Reader- فهو يوفر واجهة ملائمة للعمل مع الرموز. مع تدفقات الإخراج، تبدو المشكلة وحلها كما في حالة تدفقات الإدخال. هناك فئة OutputStreamيمكنها كتابة البايتات فقط؛ هناك فئة مجردة Writerيمكنها العمل مع الرموز، وهناك واجهتان غير متوافقتين. تم حل هذه المشكلة بنجاح مرة أخرى عن طريق نمط المحول. باستخدام الفصل، OutputStreamWriterيمكننا بسهولة "تكييف" واجهات الفصل Writerمع OutputStreamبعضها البعض. وبعد تلقي دفق بايت OutputStreamفي المُنشئ، OutputStreamWriterيمكننا، بمساعدة، كتابة الأحرف، وليس البايتات!
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException {

       OutputStreamWriter streamWriter = new OutputStreamWriter(new FileOutputStream("C:\\Users\\Username\\Desktop\\test.txt"));
       streamWriter.write(32144);
       streamWriter.close();
   }
}
لقد كتبنا حرفًا بالكود 32144 - 綐 في ملفنا، وبالتالي تخلصنا من الحاجة إلى العمل بالبايتات :) هذا كل ما لدينا اليوم، نراكم في المحاضرات القادمة! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION