CodeGym /مدونة جافا /Random-AR /نمط تصميم المحول
John Squirrels
مستوى
San Francisco

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

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

   void connectWithUsbCable();
}
هذه هي واجهة USB الخاصة بنا مع طريقة واحدة فقط للاتصال عبر USB.
public class MemoryCard {

   public void insert() {
       System.out.println("Memory card successfully inserted!");
   }

   public void copyData() {
       System.out.println("The data has been copied to the computer!");
   }
}
هذا هو صفنا الذي يمثل بطاقة الذاكرة. إنه يحتوي بالفعل على الطريقتين اللتين نحتاجهما، ولكن هنا تكمن المشكلة: فهو لا يطبق واجهة 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يفعل الفصل وما الذي يجعله محولًا بالضبط؟ كل شيء بسيط. تصبح الفئة التي يتم تكييفها (MemoryCard) أحد حقول المحول. هذا يبدو منطقيا. عندما نضع بطاقة ذاكرة داخل قارئ البطاقات في الحياة الواقعية، فإنها تصبح أيضًا جزءًا منه. وعلى عكس بطاقة الذاكرة، يشترك المحول في واجهة مع الكمبيوتر. يحتوي على كابل USB، أي أنه يمكن توصيله بأجهزة أخرى عبر USB. ولهذا السبب تقوم فئة CardReader الخاصة بنا بتنفيذ واجهة USB. ولكن ماذا يحدث بالضبط داخل هذه الطريقة؟ بالضبط ما نحتاج أن يحدث! يقوم المحول بتفويض العمل إلى بطاقة الذاكرة الخاصة بنا. في الواقع، المحول لا يفعل أي شيء بنفسه. لا يحتوي قارئ البطاقة على أي وظيفة مستقلة. وظيفتها هي فقط توصيل الكمبيوتر وبطاقة الذاكرة للسماح للبطاقة بالقيام بعملها - نسخ الملفات! يتيح المحول الخاص بنا ذلك من خلال توفير الواجهة الخاصة به ( connectWithUsbCable()الطريقة) لتلبية "احتياجات" بطاقة الذاكرة. لنقم بإنشاء برنامج عميل يحاكي الشخص الذي يريد نسخ البيانات من بطاقة الذاكرة:
public class Main {

   public static void main(String[] args) {

       USB cardReader = new CardReader(new MemoryCard());
       cardReader.connectWithUsbCable();
   }
}
وذلك ما لم نحصل؟ إخراج وحدة التحكم:

Memory card successfully inserted! 
The data has been copied to the computer!
ممتاز. لقد حققنا هدفنا! إليك رابط لمقطع فيديو يحتوي على معلومات حول نمط المحول:

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

سنعود الآن إلى نشاطنا المفضل: التعرف على فصلين جديدين للعمل مع الإدخال والإخراج :) وأتساءل عن عدد الفصول التي تعلمنا عنها بالفعل. اليوم سنتحدث عن الطبقات 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الواجهات  Readerغير متوافقة! كما ترون، كل تطبيق لهذه read()الطريقة له معلمات وقيم إرجاع مختلفة. وهذا هو المكان الذي نحتاجه InputStreamReader! سيكون بمثابة محول بين فصولنا. كما في مثال قارئ البطاقة، الذي تناولناه أعلاه، وضعنا مثيلًا للفئة التي تم تكييفها "داخل" فئة المحول، أي أننا قمنا بتمرير واحدة إلى مُنشئها. في المثال السابق، قمنا بوضع MemoryCardكائن داخل CardReader. الآن نقوم بتمرير InputStream كائن إلى InputStreamReaderالمنشئ! نستخدم System.inالمتغير المألوف لدينا كـ InputStream:
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