هل يجب علي استخدام الاستنساخ عند إضافة عنصر جديد؟متى يجب استخدام الاستنساخ؟

StackOverflow https://stackoverflow.com/questions/63748

  •  09-06-2019
  •  | 
  •  

سؤال

أرغب في تنفيذ فئة في Java للتعامل مع هياكل بيانات الرسم البياني.لدي فئة العقدة وفئة الحافة.تحتفظ فئة الرسم البياني بقائمتين:قائمة العقد وقائمة الحواف.يجب أن يكون لكل عقدة اسم فريد.كيف أحترس من موقف مثل هذا:

Graph g = new Graph();

Node n1 = new Node("#1");
Node n2 = new Node("#2");

Edge e1 = new Edge("e#1", "#1", "#2");

// Each node is added like a reference
g.addNode(n1);
g.addNode(n2);
g.addEdge(e1);

// This will break the internal integrity of the graph
n1.setName("#3");   
g.getNode("#2").setName("#4"); 

أعتقد أنه يجب علي استنساخ العقد والحواف عند إضافتها إلى الرسم البياني وإرجاع فئة NodeEnvelope التي ستحافظ على السلامة الهيكلية للرسم البياني.هل هذه هي الطريقة الصحيحة للقيام بذلك أم أن التصميم معطل من البداية؟

هل كانت مفيدة؟

المحلول

أنا أعمل مع هياكل الرسم البياني في Java كثيرًا، ونصيحتي هي جعل أي عضو بيانات في فئة Node وEdge التي يعتمد عليها الرسم البياني للحفاظ على هيكله نهائيًا، بدون أدوات ضبط.في الواقع، إذا استطعت، سأجعل Node وEdge غير قابلين للتغيير تمامًا، وهو ما حدث العديد من الفوائد.

لذلك، على سبيل المثال:

public final class Node {

    private final String name;

    public Node(String name) {
           this.name = name;
    }

    public String getName() { return name; }
    // note: no setter for name
}

يمكنك بعد ذلك التحقق من التفرد في كائن الرسم البياني:

public class Graph {
    Set<Node> nodes = new HashSet<Node>();
    public void addNode(Node n) {
        // note: this assumes you've properly overridden 
        // equals and hashCode in Node to make Nodes with the 
        // same name .equal() and hash to the same value.
        if(nodes.contains(n)) {
            throw new IllegalArgumentException("Already in graph: " + node);
        }
        nodes.add(n);
    }
}

إذا كنت بحاجة إلى تعديل اسم العقدة، فقم بإزالة العقدة القديمة وأضف واحدة جديدة.قد يبدو هذا بمثابة عمل إضافي، لكنه يوفر الكثير من الجهد للحفاظ على كل شيء في نصابه الصحيح.

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

أوصي بإيجاد مكتبة جيدة للرسوم البيانية مفتوحة المصدر لـ Java، واستخدامها بدلاً من ذلك.اعتمادا على ما تفعله، هناك عدد قليل من الخيارات المتاحة.لقد استخدمت يونغ في الماضي، وسوف أوصي به كنقطة انطلاق جيدة.

نصائح أخرى

ليس من الواضح بالنسبة لي سبب قيامك بإضافة الاتجاه الإضافي لأسماء السلسلة للعقد.ألن يكون من المنطقي أكثر أن يكون توقيع مُنشئ Edge الخاص بك شيئًا كهذا public Edge(String, Node, Node) بدلاً من public Edge (String, String, String)?

لا أعرف أين سيساعدك الاستنساخ هنا.

الوقت المتوقع للوصول:إذا كان الخطر يأتي من تغيير اسم العقدة بعد إنشاء العقدة، فقم برمي IllegalOperationException إذا حاول العميل استدعاء setName() على عقدة ذات اسم موجود.

في رأيي، لا يجب عليك أبدًا استنساخ العنصر إلا إذا ذكرت صراحةً أن بنية البيانات الخاصة بك تقوم بذلك.

تحتاج الوظيفة المطلوبة لمعظم الأشياء إلى تمرير الكائن الفعلي إلى بنية البيانات حسب المرجع.

إذا كنت تريد أن تجعل Node فئة أكثر أمانا، وجعلها فئة داخلية للرسم البياني.

يبدو استخدام NodeEnvelopes أو edge/node Factories بمثابة تصميم زائد بالنسبة لي.

هل تريد حقًا الكشف عن طريقة setName() على Node على الإطلاق؟لا يوجد شيء في المثال الخاص بك يشير إلى أنك بحاجة إلى ذلك.إذا قمت بجعل فئتي Node وEdge غير قابلة للتغيير، فإن معظم سيناريوهات انتهاك السلامة التي تتصورها تصبح مستحيلة.(إذا كنت تريد أن تكون قابلة للتغيير ولكن فقط حتى تتم إضافتها إلى الرسم البياني، فيمكنك فرض ذلك من خلال وجود علامة isInGraph على فئات Node/Edge الخاصة بك والتي تم تعيينها على true بواسطة Graph.Add{Node, Edge}، و اطلب من المتحولين لديك طرح استثناء إذا تم استدعاؤهم بعد تعيين هذه العلامة.)

أتفق مع jhkiley في أن تمرير كائنات Node إلى مُنشئ Edge (بدلاً من Strings) يبدو فكرة جيدة.

إذا كنت تريد نهجًا أكثر تدخلاً، فيمكنك الحصول على مؤشر من فئة العقدة للعودة إلى الرسم البياني الموجود فيه، وتحديث الرسم البياني في حالة تغيير أي خصائص مهمة (على سبيل المثال، الاسم) للعقدة.لكنني لن أفعل ذلك إلا إذا كنت متأكدًا من أنك بحاجة إلى أن تكون قادرًا على تغيير أسماء العقد الموجودة مع الحفاظ على علاقات Edge، وهو ما يبدو غير مرجح.

يواجه Object.clone() بعض المشكلات الرئيسية، ولا يُنصح باستخدامه في معظم الحالات.الرجاء مراجعة البند 11 من "جافا فعالة"بقلم جوشوا بلوخ للحصول على إجابة كاملة.أعتقد أنه يمكنك استخدام Object.clone() بأمان على صفائف النوع البدائي، ولكن بصرف النظر عن ذلك، يجب أن تكون حكيمًا بشأن استخدام النسخ وتجاوزه بشكل صحيح.ربما يكون من الأفضل لك تحديد مُنشئ نسخة أو طريقة مصنع ثابتة تستنسخ الكائن بشكل صريح وفقًا لدلالاتك.

بالإضافة إلى تعليقات @jhkiley.blogspot.com، يمكنك إنشاء مصنع للحواف والعقد يرفض إنشاء كائنات باسم تم استخدامه بالفعل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top