JavaRush /مدونة جافا /Random-AR /أنماط التصميم في جافا
Viacheslav
مستوى

أنماط التصميم في جافا

نشرت في المجموعة
غالبًا ما تكون الأنماط أو أنماط التصميم جزءًا يتم تجاهله من وظيفة المطور، مما يجعل من الصعب الحفاظ على التعليمات البرمجية والتكيف مع المتطلبات الجديدة. أقترح عليك إلقاء نظرة على ماهيته وكيفية استخدامه في JDK. وبطبيعة الحال، كانت جميع الأنماط الأساسية بشكل أو بآخر موجودة حولنا لفترة طويلة. دعونا نراهم في هذه المراجعة.
أنماط التصميم في جافا - 1
محتوى:

قوالب

أحد المتطلبات الأكثر شيوعًا في الوظائف الشاغرة هو "المعرفة بالأنماط". بادئ ذي بدء، من المفيد الإجابة على سؤال بسيط - "ما هو نمط التصميم؟" تتم ترجمة النمط من اللغة الإنجليزية باسم "قالب". أي أن هذا نمط معين نفعل وفقًا له شيئًا ما. إنه نفس الشيء في البرمجة. هناك بعض أفضل الممارسات والأساليب المتبعة لحل المشكلات الشائعة. كل مبرمج هو مهندس معماري. حتى عندما تقوم بإنشاء عدد قليل من الفئات أو حتى فئة واحدة فقط، يعتمد الأمر عليك على المدة التي يمكن أن يستمر فيها الكود في ظل المتطلبات المتغيرة، ومدى ملاءمة استخدامه من قبل الآخرين. وهذا هو المكان الذي ستساعدك فيه معرفة القوالب، لأن... سيتيح لك ذلك أن تفهم بسرعة أفضل السبل لكتابة التعليمات البرمجية دون إعادة كتابتها. كما تعلم، المبرمجون أشخاص كسالى ومن الأسهل كتابة شيء جيد على الفور بدلاً من إعادته عدة مرات) قد تبدو الأنماط أيضًا مشابهة للخوارزميات. لكن لديهم فرق. تتكون الخوارزمية من خطوات محددة تصف الإجراءات الضرورية. تصف الأنماط النهج فقط، ولكنها لا تصف خطوات التنفيذ. الأنماط مختلفة، لأن... حل مشاكل مختلفة. عادة ما يتم تمييز الفئات التالية:
  • توليدي

    تحل هذه الأنماط مشكلة جعل إنشاء الكائنات مرنًا

  • الهيكلي

    تحل هذه الأنماط مشكلة بناء الروابط بين الكائنات بشكل فعال

  • سلوكية

    تحل هذه الأنماط مشكلة التفاعل الفعال بين الأشياء

للنظر في الأمثلة، أقترح استخدام مترجم التعليمات البرمجية عبر الإنترنت repl.it.
أنماط التصميم في جافا - 2

الأنماط الإبداعية

لنبدأ من بداية دورة حياة الكائنات - مع إنشاء الكائنات. تساعد القوالب التوليدية في إنشاء الكائنات بسهولة أكبر وتوفر المرونة في هذه العملية. ومن أشهرها " الباني ". يتيح لك هذا النمط إنشاء كائنات معقدة خطوة بخطوة. في جافا، المثال الأكثر شهرة هو StringBuilder:
class Main {
  public static void main(String[] args) {
    StringBuilder builder = new StringBuilder();
    builder.append("Hello");
    builder.append(',');
    builder.append("World!");
    System.out.println(builder.toString());
  }
}
هناك طريقة أخرى معروفة لإنشاء كائن وهي نقل الإنشاء إلى طريقة منفصلة. تصبح هذه الطريقة، كما كانت، مصنعًا للأشياء. ولهذا السبب يسمى النمط " طريقة المصنع ". في Java، على سبيل المثال، يمكن رؤية تأثيره في الفصل java.util.Calendar. الفئة نفسها Calendarمجردة، ولإنشائها يتم استخدام الطريقة getInstance:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Calendar calendar = Calendar.getInstance();
    System.out.println(calendar.getTime());
    System.out.println(calendar.getClass().getCanonicalName());
  }
}
ويرجع ذلك غالبًا إلى أن المنطق الكامن وراء إنشاء الكائن قد يكون معقدًا. على سبيل المثال، في الحالة أعلاه، نصل إلى الفئة الأساسية Calendar، ويتم إنشاء فئة GregorianCalendar. إذا نظرنا إلى المُنشئ، يمكننا أن نرى أنه يتم إنشاء تطبيقات مختلفة اعتمادًا على الظروف Calendar. لكن في بعض الأحيان لا تكون طريقة المصنع الواحدة كافية. في بعض الأحيان تحتاج إلى إنشاء كائنات مختلفة بحيث تتناسب معًا. سيساعدنا قالب آخر في هذا - " مصنع الملخص ". ومن ثم نحتاج إلى إنشاء مصانع مختلفة في مكان واحد. وفي نفس الوقت الميزة هي أن تفاصيل التنفيذ ليست مهمة بالنسبة لنا، أي. لا يهم المصنع المحدد الذي نحصل عليه. الشيء الرئيسي هو أنه يخلق التطبيقات الصحيحة. مثال رائع:
أنماط التصميم في جافا - 3
وهذا يعني، اعتمادًا على البيئة (نظام التشغيل)، سنتلقى مصنعًا معينًا يقوم بإنشاء عناصر متوافقة. كبديل لنهج الإنشاء من خلال شخص آخر، يمكننا استخدام نمط " النموذج الأولي ". جوهرها بسيط - يتم إنشاء كائنات جديدة في صورة ومثال الكائنات الموجودة بالفعل، أي. وفقا لنموذجهم الأولي. في Java، واجه الجميع هذا النمط - وهذا هو استخدام الواجهة java.lang.Cloneable:
class Main {
  public static void main(String[] args) {
    class CloneObject implements Cloneable {
      @Override
      protected Object clone() throws CloneNotSupportedException {
        return new CloneObject();
      }
    }
    CloneObject obj = new CloneObject();
    try {
      CloneObject pattern = (CloneObject) obj.clone();
    } catch (CloneNotSupportedException e) {
      //Do something
    }
  }
}
كما ترون، المتصل لا يعرف كيف clone. أي أن إنشاء كائن بناءً على نموذج أولي هو مسؤولية الكائن نفسه. وهذا مفيد لأنه لا يربط المستخدم بتنفيذ كائن القالب. حسنًا، آخر نمط في هذه القائمة هو نمط "Singleton". والغرض منه بسيط - توفير مثيل واحد للكائن للتطبيق بأكمله. يعد هذا النمط مثيرًا للاهتمام لأنه غالبًا ما يُظهر مشكلات تعدد العمليات. للحصول على نظرة أكثر تعمقا، راجع هذه المقالات:
أنماط التصميم في جافا - 4

الأنماط الهيكلية

مع خلق الأشياء أصبح الأمر أكثر وضوحا. الآن هو الوقت المناسب للنظر في الأنماط الهيكلية. هدفهم هو بناء تسلسلات هرمية للفصل الدراسي وعلاقاتهم سهلة الدعم. أحد الأنماط الأولى والمعروفة هو " النائب " (الوكيل). يحتوي الوكيل على نفس واجهة الكائن الحقيقي، لذلك لا يوجد فرق بالنسبة للعميل للعمل من خلال الوكيل أو مباشرة. أبسط مثال هو java.lang.reflect.Proxy :
import java.util.*;
import java.lang.reflect.*;
class Main {
  public static void main(String[] arguments) {
    final Map<String, String> original = new HashMap<>();
    InvocationHandler proxy = (obj, method, args) -> {
      System.out.println("Invoked: " + method.getName());
      return method.invoke(original, args);
    };
    Map<String, String> proxyInstance = (Map) Proxy.newProxyInstance(
        original.getClass().getClassLoader(),
        original.getClass().getInterfaces(),
        proxy);
    proxyInstance.put("key", "value");
    System.out.println(proxyInstance.get("key"));
  }
}
كما ترون، في المثال لدينا الأصلي - وهذا هو HashMapالذي ينفذ الواجهة Map. نقوم بعد ذلك بإنشاء وكيل يحل محل الوكيل الأصلي HashMapلجزء العميل، والذي يستدعي putالأساليب get، ويضيف المنطق الخاص بنا أثناء المكالمة. كما نرى، التفاعل في النمط يحدث من خلال الواجهات. لكن في بعض الأحيان لا يكون البديل كافيا. ومن ثم يمكن استخدام نمط " الديكور ". يُطلق على مصمم الديكور أيضًا اسم الغلاف أو الغلاف. الوكيل والديكور متشابهان جدًا، ولكن إذا نظرت إلى المثال، سترى الفرق:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    List<String> list = new ArrayList<>();
    List<String> decorated = Collections.checkedList(list, String.class);
    decorated.add("2");
    list.add("3");
    System.out.println(decorated);
  }
}
على عكس الوكيل، يلتف مصمم الديكور حول شيء يتم تمريره كمدخل. يمكن للوكيل قبول ما يحتاج إلى وكيل وكذلك إدارة عمر الكائن الوكيل (على سبيل المثال، إنشاء كائن وكيل). هناك نمط آخر مثير للاهتمام - " المحول ". إنه مشابه للديكور - يأخذ الديكور كائنًا واحدًا كمدخل ويعيد غلافًا فوق هذا الكائن. والفرق هو أن الهدف ليس تغيير الوظيفة، بل تكييف واجهة مع أخرى. جافا لديها مثال واضح جدا على ذلك:
import java.util.*;
class Main {
  public static void main(String[] arguments) {
    String[] array = {"One", "Two", "Three"};
    List<String> strings = Arrays.asList(array);
    strings.set(0, "1");
    System.out.println(Arrays.toString(array));
  }
}
عند الإدخال لدينا مجموعة. بعد ذلك نقوم بإنشاء محول ينقل المصفوفة إلى الواجهة List. عندما نعمل معها، فإننا في الواقع نعمل مع مصفوفة. ولذلك فإن إضافة العناصر لن ينجح، لأن... لا يمكن تغيير المصفوفة الأصلية. وفي هذه الحالة سوف نحصل UnsupportedOperationException. النهج التالي المثير للاهتمام لتطوير بنية الفصل هو النمط المركب . ومن المثير للاهتمام أن مجموعة معينة من العناصر التي تستخدم واجهة واحدة يتم ترتيبها في تسلسل هرمي معين يشبه الشجرة. من خلال استدعاء أسلوب على عنصر أصل، نحصل على استدعاء لهذا الأسلوب على جميع العناصر الفرعية الضرورية. ومن الأمثلة الرئيسية على هذا النمط واجهة المستخدم (سواء كانت java.awt أو JSF):
import java.awt.*;
class Main {
  public static void main(String[] arguments) {
    Container container = new Container();
    Component component = new java.awt.Component(){};
    System.out.println(component.getComponentOrientation().isLeftToRight());
    container.add(component);
    container.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
    System.out.println(component.getComponentOrientation().isLeftToRight());
  }
}
كما نرى، قمنا بإضافة مكون إلى الحاوية. ثم طلبنا من الحاوية تطبيق الاتجاه الجديد للمكونات. والحاوية، التي تعرف المكونات التي تتكون منها، فوضت تنفيذ هذا الأمر إلى جميع المكونات الفرعية. نمط آخر مثير للاهتمام هو نمط " الجسر ". يطلق عليه هذا لأنه يصف الاتصال أو الجسر بين تسلسلين هرميين مختلفين. يعتبر أحد هذه التسلسلات الهرمية تجريدًا والآخر تطبيقًا. يتم تسليط الضوء على هذا لأن التجريد نفسه لا ينفذ الإجراءات، ولكنه يفوض هذا التنفيذ إلى التنفيذ. يُستخدم هذا النمط غالبًا عندما تكون هناك فئات "تحكم" وأنواع متعددة من فئات "النظام الأساسي" (على سبيل المثال، Windows وLinux وما إلى ذلك). باستخدام هذا النهج، ستتلقى إحدى هذه التسلسلات الهرمية (التجريد) إشارة إلى كائنات من تسلسل هرمي آخر (التنفيذ) وستفوض العمل الرئيسي إليها. نظرًا لأن جميع التطبيقات ستتبع واجهة مشتركة، فيمكن تبادلها داخل التجريد. في جافا، مثال واضح على ذلك هو java.awt:
أنماط التصميم في جافا - 5
لمزيد من المعلومات، راجع المقالة " الأنماط في Java AWT ". من بين الأنماط الهيكلية، أود أيضًا أن أشير إلى نمط " الواجهة ". جوهرها هو إخفاء تعقيد استخدام المكتبات/أطر العمل خلف واجهة برمجة التطبيقات هذه خلف واجهة مريحة وموجزة. على سبيل المثال، يمكنك استخدام JSF أو EntityManager من JPA كمثال. هناك أيضًا نمط آخر يسمى " وزن الذبابة ". جوهرها هو أنه إذا كانت الكائنات المختلفة لها نفس الحالة، فيمكن تعميمها وتخزينها ليس في كل كائن، ولكن في مكان واحد. ومن ثم سيكون كل كائن قادرًا على الرجوع إلى جزء مشترك، مما يقلل من تكاليف الذاكرة للتخزين. يتضمن هذا النمط غالبًا التخزين المؤقت المسبق أو الاحتفاظ بمجموعة من الكائنات. ومن المثير للاهتمام أننا نعرف أيضًا هذا النمط منذ البداية:
أنماط التصميم في جافا - 6
وبنفس القياس، يمكن تضمين مجموعة من السلاسل هنا. يمكنك قراءة المقال حول هذا الموضوع: " نمط تصميم وزن الذبابة ".
أنماط التصميم في جافا - 7

الأنماط السلوكية

لذلك، اكتشفنا كيف يمكن إنشاء الكائنات وكيفية تنظيم الاتصالات بين الفئات. الشيء الأكثر إثارة للاهتمام المتبقي هو توفير المرونة في تغيير سلوك الأشياء. وسوف تساعدنا الأنماط السلوكية في ذلك. أحد الأنماط الأكثر ذكرًا هو نمط " الإستراتيجية ". ومن هنا تبدأ دراسة الأنماط في كتاب " الرأس أولاً. أنماط التصميم ". باستخدام نمط "الاستراتيجية"، يمكننا تخزين داخل الكائن كيفية تنفيذ الإجراء، أي. يقوم الكائن الموجود بالداخل بتخزين إستراتيجية يمكن تغييرها أثناء تنفيذ التعليمات البرمجية. هذا هو النمط الذي نستخدمه غالبًا عند استخدام المقارنة:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Comparator<String> comparator = Comparator.comparingInt(String::length);
    Set dataSet = new TreeSet(comparator);
    dataSet.addAll(data);
    System.out.println("Dataset : " + dataSet);
  }
}
قبلنا - TreeSet. لديه سلوك TreeSetالحفاظ على ترتيب العناصر، أي. يفرزها (نظرًا لأنها مجموعة SortedSet). يحتوي هذا السلوك على استراتيجية افتراضية، والتي نراها في JavaDoc: الفرز "بالترتيب الطبيعي" (بالنسبة للسلاسل، هذا هو الترتيب المعجمي). يحدث هذا إذا كنت تستخدم مُنشئًا بدون معلمات. ولكن إذا أردنا تغيير الاستراتيجية، يمكننا أن نمرر Comparator. في هذا المثال، يمكننا إنشاء مجموعتنا كـ new TreeSet(comparator)، ثم يتغير ترتيب تخزين العناصر (استراتيجية التخزين) إلى الترتيب المحدد في المقارنة. ومن المثير للاهتمام أن هناك تقريبًا نفس النمط المسمى " الدولة ". يقول نمط "الحالة" أنه إذا كان لدينا بعض السلوك في الكائن الرئيسي الذي يعتمد على حالة هذا الكائن، فيمكننا وصف الحالة نفسها ككائن وتغيير كائن الحالة. ومندوب يدعو من الكائن الرئيسي إلى الدولة. هناك نمط آخر معروف لنا من خلال دراسة أساسيات لغة Java وهو نمط " Command ". يشير نمط التصميم هذا إلى أنه يمكن تمثيل الأوامر المختلفة كفئات مختلفة. هذا النمط مشابه جدًا لنمط الإستراتيجية. ولكن في نمط الإستراتيجية، كنا نعيد تعريف كيفية تنفيذ إجراء معين (على سبيل المثال، الفرز TreeSet). في نمط "الأمر"، نعيد تحديد الإجراء الذي سيتم تنفيذه. أمر النمط موجود معنا كل يوم عندما نستخدم سلاسل الرسائل:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Runnable command = () -> {
      System.out.println("Command action");
    };
    Thread th = new Thread(command);
    th.start();
  }
}
كما ترى، يحدد الأمر الإجراء أو الأمر الذي سيتم تنفيذه في سلسلة رسائل جديدة. ومن الجدير أيضًا النظر في نمط " سلسلة المسؤولية ". هذا النمط أيضًا بسيط جدًا. يشير هذا النمط إلى أنه إذا كان هناك شيء يحتاج إلى المعالجة، فيمكنك جمع المعالجات في سلسلة. على سبيل المثال، غالبا ما يستخدم هذا النمط في خوادم الويب. عند الإدخال، لدى الخادم بعض الطلبات من المستخدم. ثم يمر هذا الطلب عبر سلسلة المعالجة. تتضمن سلسلة المعالجات هذه عوامل التصفية (على سبيل المثال، عدم قبول الطلبات من قائمة سوداء لعناوين IP)، ومعالجات المصادقة (السماح للمستخدمين المصرح لهم فقط)، ومعالج رأس الطلب، ومعالج التخزين المؤقت، وما إلى ذلك. ولكن هناك مثال أبسط وأكثر قابلية للفهم في Java java.util.logging:
import java.util.logging.*;
class Main {
  public static void main(String[] args) {
    Logger logger = Logger.getLogger(Main.class.getName());
    ConsoleHandler consoleHandler = new ConsoleHandler(){
		@Override
            public void publish(LogRecord record) {
                System.out.println("LogRecord обработан");
            }
        };
    logger.addHandler(consoleHandler);
    logger.info("test");
  }
}
كما ترون، تتم إضافة المعالجات إلى قائمة معالجات المسجل. عندما يتلقى المسجل رسالة للمعالجة، تمر كل رسالة من هذا القبيل عبر سلسلة من المعالجات (من logger.getHandlers) لذلك المسجل. نمط آخر نراه كل يوم هو " التكرار ". جوهرها هو فصل مجموعة من الكائنات (أي فئة تمثل بنية البيانات. على سبيل المثال، List) واجتياز هذه المجموعة.
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> data = Arrays.asList("Moscow", "Paris", "NYC");
    Iterator<String> iterator = data.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }
  }
}
كما ترون، المكرر ليس جزءًا من المجموعة، ولكن يتم تمثيله بفئة منفصلة تعبر المجموعة. قد لا يعرف مستخدم المُكرِّر حتى المجموعة التي يتكرر عليها، على سبيل المثال. ما هي المجموعة التي يزورها؟ نمط الزائر يستحق النظر فيه أيضًا . نمط الزائر مشابه جدًا لنمط التكرار. يساعدك هذا النمط على تجاوز بنية الكائنات وتنفيذ إجراءات على هذه الكائنات. إنهم يختلفون إلى حد ما في المفهوم. يجتاز المكرّر المجموعة بحيث لا يهتم العميل الذي يستخدم المكرّر بما تحتويه المجموعة، فقط العناصر الموجودة في التسلسل هي المهمة. الزائر يعني أن هناك تسلسلًا هرميًا أو هيكلًا معينًا للأشياء التي نزورها. على سبيل المثال، يمكننا استخدام معالجة منفصلة للدليل ومعالجة منفصلة للملفات. لدى Java تطبيق خارج الصندوق لهذا النمط في النموذج java.nio.file.FileVisitor:
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.io.*;
class Main {
  public static void main(String[] args) {
    SimpleFileVisitor visitor = new SimpleFileVisitor() {
      @Override
      public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
        System.out.println("File:" + file.toString());
        return FileVisitResult.CONTINUE;
      }
    };
    Path pathSource = Paths.get(System.getProperty("java.io.tmpdir"));
    try {
      Files.walkFileTree(pathSource, visitor);
    } catch (AccessDeniedException e) {
      // skip
    } catch (IOException e) {
      // Do something
    }
  }
}
في بعض الأحيان تكون هناك حاجة لبعض الكائنات للتفاعل مع التغييرات في كائنات أخرى، ومن ثم سيساعدنا نمط "المراقب " . الطريقة الأكثر ملاءمة هي توفير آلية اشتراك تسمح لبعض الكائنات بمراقبة الأحداث التي تحدث في كائنات أخرى والاستجابة لها. غالبًا ما يستخدم هذا النمط لدى العديد من المستمعين والمراقبين الذين يتفاعلون مع أحداث مختلفة. وكمثال بسيط، يمكننا أن نتذكر تنفيذ هذا النمط من الإصدار الأول من JDK:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Observer observer = (obj, arg) -> {
      System.out.println("Arg: " + arg);
    };
    Observable target = new Observable(){
      @Override
      public void notifyObservers(Object arg) {
        setChanged();
        super.notifyObservers(arg);
      }
    };
    target.addObserver(observer);
    target.notifyObservers("Hello, World!");
  }
}
هناك نمط سلوكي مفيد آخر - " الوسيط ". إنه مفيد لأنه في الأنظمة المعقدة يساعد على إزالة الاتصال بين الكائنات المختلفة وتفويض جميع التفاعلات بين الكائنات إلى كائن ما، وهو وسيط. أحد التطبيقات الأكثر لفتًا للانتباه لهذا النمط هو Spring MVC، الذي يستخدم هذا النمط. يمكنك قراءة المزيد عن هذا هنا: " الربيع: نمط الوسيط ". يمكنك غالبًا رؤية الشيء نفسه في الأمثلة java.util.Timer:
import java.util.*;
class Main {
  public static void main(String[] args) {
    Timer mediator = new Timer("Mediator");
    TimerTask command = new TimerTask() {
      @Override
      public void run() {
        System.out.println("Command pattern");
        mediator.cancel();
      }
    };
    mediator.schedule(command, 1000);
  }
}
يبدو المثال أشبه بنمط الأمر. وجوهر نمط "الوسيط" مخفي في تنفيذ Timer"أ". يوجد داخل المؤقت قائمة انتظار مهام TaskQueue، ويوجد خيط TimerThread. نحن، كعملاء لهذه الفئة، لا نتفاعل معهم، بل نتفاعل مع Timerالكائن، الذي، استجابة لدعوتنا إلى أساليبه، يصل إلى أساليب الكائنات الأخرى التي هو وسيط لها. خارجيًا قد يبدو مشابهًا جدًا لـ "الواجهة". لكن الفرق هو أنه عند استخدام الواجهة، لا تعرف المكونات وجود الواجهة وتتحدث مع بعضها البعض. وعند استخدام "الوسيط"، تعرف المكونات الوسيط وتستخدمه، لكن لا تتصل ببعضها البعض بشكل مباشر. يجدر النظر في نمط " طريقة القالب "، النمط واضح من اسمه. خلاصة القول هي أن الكود مكتوب بطريقة يتم من خلالها تزويد مستخدمي الكود (المطورين) ببعض قوالب الخوارزمية، والتي يُسمح بإعادة تعريف خطواتها. يتيح ذلك لمستخدمي التعليمات البرمجية عدم كتابة الخوارزمية بأكملها، ولكن التفكير فقط في كيفية تنفيذ خطوة أو أخرى من هذه الخوارزمية بشكل صحيح. على سبيل المثال، تحتوي Java على فئة مجردة AbstractListتحدد سلوك المكرر بواسطة List. ومع ذلك، يستخدم المكرِّر نفسه أساليب ورقية مثل: get, set, remove. يتم تحديد سلوك هذه الأساليب من قبل المطور للأحفاد AbstractList. وبالتالي، فإن المكرر في AbstractList- هو قالب لخوارزمية التكرار على الورقة. ويقوم مطورو تطبيقات محددة AbstractListبتغيير سلوك هذا التكرار من خلال تحديد سلوك خطوات محددة. آخر الأنماط التي نقوم بتحليلها هو نمط " اللقطة " (Momento). جوهرها هو الحفاظ على حالة معينة للكائن مع القدرة على استعادة هذه الحالة. المثال الأكثر شهرة من JDK هو تسلسل الكائنات، أي. java.io.Serializable. لنلقي نظرة على مثال:
import java.io.*;
import java.util.*;
class Main {
  public static void main(String[] args) throws IOException {
    ArrayList<String> list = new ArrayList<>();
    list.add("test");
    // Save State
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
      out.writeObject(list);
    }
    // Load state
    byte[] bytes = stream.toByteArray();
    InputStream inputStream = new ByteArrayInputStream(bytes);
    try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
      List<String> listNew = (List<String>) in.readObject();
      System.out.println(listNew.get(0));
    } catch (ClassNotFoundException e) {
      // Do something. Can't find class fpr saved state
    }
  }
}
أنماط التصميم في جافا - 8

خاتمة

كما رأينا من المراجعة، هناك مجموعة كبيرة ومتنوعة من الأنماط. كل واحد منهم يحل مشكلته الخاصة. ومعرفة هذه الأنماط يمكن أن تساعدك على فهم كيفية كتابة نظامك في الوقت المناسب بحيث يكون مرنًا وقابلاً للصيانة ومقاومًا للتغيير. وأخيرًا، بعض الروابط للتعمق أكثر: # فياتشيسلاف
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION