جافا: كيفية التعامل مع الكثير من الحقول وتغليفها نظيفة؟

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

  •  12-09-2019
  •  | 
  •  

سؤال

دعنا نقول أنني مهمت مع ترميز نوع من آر بي جي. هذا يعني أنه على سبيل المثال، سأرغب في تتبع Character GameCharacter وإحصائياتها، مثل الذكاء أو مكافآت الضرر أو نقاط ضرب.

أخشى إيجابيا أنه بحلول نهاية المشروع، قد ينتهي الأمر بالتعامل مع عدد كبير جدا من الحقول - ولكل منهما للتأكد من اتباع مجموعة مماثلة للغاية من القيد والسلوكيات (على سبيل المثال، أنا هل تريد منهم أن تكون حادا بين دقيقة وبحد أقصى؛ أريد أن أكون قادرا على التمييز بين "القيمة الأساسية" و "مكافأة مؤقتة"؛ أريد أن أكون قادرا على زيادة الزيادة وانخفاض كلاهما دون المرور عبر setters and getters) وبعد فجأة، بالنسبة لكل مجال أحتاج إلى واحد (اثنين؟) الحصول على أربع أشخاص وأربعة أشخاص وربما عفة زوجين أيضا! حتى لمدة 10 حقول تعني الكثير من الطرق على حد سواء، EEK.

لجافة بدأت تغليف منطق العبث مع تلك الإحصائيات في Field الطبقات، بحيث يمكنني كتابة رمز مثل intelligence.applyBonus(10) أو hitpoints.get() (الذي يأخذ الرعاية القيمة التي تم إرجاعها هي في النطاق)، إلخ. لقد ذهبت إلى طول هذا الطول لإنشاء فصول لتجميع هذه الحقول معا، ولكن هذه ليست النقطة الآن.

الآن، أصبت في هذه المشكلة أثناء "توصيل" Field إلى GameCharacter: تقول معظم الكتب المدرسية Java أن كل فئة يجب أن تحتوي على حقول خاصة مع Getters و Setters الجمهوريين. هذا يبدو جيدا من الناحية النظرية، وقد بنيت بالفعل فئة كاملة حول int; ؛ ومع ذلك، فإن الفكرة لا تبدو صلبة عندما تجد نفسك تسمي Getter للحصول على ... Getter:

thisCharacter.getIntelligence().get() //eeek

كنت هناك الكثير من الوصول إلى الحقل مباشرة. ربما يكون الأمر بيثون / VB [1] "الخلفية"، ولكن بالنسبة لي هي أنظف، أكثر وضوحا ومباشرة:

thisCharacter.intelligence.get()

المشكلة (النظرية) مع الحقول العامة هي أنني أتخلى عن كل السيطرة عليها؛ على سبيل المثال في بعض النقاط الأخرى في CodeBase، حسب سوء الحظ، قد يحدث ما يلي:

thisCharacter.intelligence = somethingThatReallyIsNull;

يبدو وكأنه علة خفية ... ولكن ... أعني، هل يجب أن أشعر بالقلق حقا بذلك؟ أنا لأحد لا يخطط أبدا لتعيين Field مباشرة [2]، لقد وثقت في Javadoc أن هذا ليس شيئا يجب القيام به، ولكن لا يزال أنا جديد هنا لذلك أنا ممزقة بعض الشيء.

لذلك أود أن أسمع ما تأخذك في هذا الموضوع. هل مزايا التغليف ضخمة للغاية لدرجة أنني يجب أن أذهب إلى الأمام ولديها Getter Getters و Serve Staint وهلم جرا ... أو يجب أن أتغلب على تدابير صحية واتركها Field ك public حقل؟


1] نعم، أعرف. لقد كنت أحاول أن أنسى. لكننا شاهدنا مؤخرا أيضا قليلا من C # والرجل، ليست محاصرة حلوة. اوه حسنا.

2] إلا في المنشئين! ولن ينقذني Getter من منشئ معيب.

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

المحلول

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

على سبيل المثال، تحتوي على خريطة سمة من المفاتيح إلى القيم.

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

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

بالنسبة للوصول إلى الحقول مباشرة، إنها فكرة سيئة. عند الوصول إلى حقل في VB (على الأقل في VBS القديم)، يمكنك عادة استدعاء Getter خاصية و STERTER و VB ببساطة يقوم ببساطة بالاتصال () دعوة لك. رأيي هو أنه يجب عليك التكيف مع اتفاقيات اللغة التي تستخدمها. في C، C ++، Java وما شابه ذلك لديك حقول ولديك طرق. يجب أن يكون لدى الدعوة من الطريقة دائما () توضح أنها مكالمة وأشياء أخرى قد تحدث (على سبيل المثال، يمكنك الحصول على استثناء). في كلتا الحالتين، واحدة من فوائد جافا هي بناء جملة وأسلوبها أكثر دقة.

VB إلى Java أو C ++ هو مثل الرسائل النصية للكتابة العلمية في المدرسة العليا.

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

نصائح أخرى

يبدو أنك تفكر من حيث if(player.dexterity > monster.dexterity) attacker = player. وبعد تحتاج إلى التفكير أكثر مثل if(player.quickerThan(monster)) monster.suffersAttackFrom(player.getCurrentWeapon()). وبعد لا تعبث بإحصائيات عارية، معبرا عن نيته الفعلية ثم تصميم فصولك حول ما يفترض القيام به. احصائيات هي شرطي خارج على أي حال؛ ما تهتم به حقا هو ما إذا كان لاعب يمكن أو لا يستطيع القيام ببعض الإجراءات، أو قدرتها / قدرتها مقابل بعض المرجع. فكر من حيث "شخصية اللاعب قوية بما فيه الكفاية" (player.canOpen(trapDoor)) بدلا من "هل تحتوي الشخصية على 50 قوة على الأقل".

كان لدى Steve Yegge منشور مدونات ممتع للغاية (إذا كان طويلا) تغطي هذه المشكلات: نمط التصميم العالمي.

بالنسبة لي يبدو وكأنه "هذا الشارقة" قد يكون لديه كائن "ذكاء" للتعامل مع الذكاء وراء الكواليس، لكنني أشك في ما إذا كان ينبغي أن يكون ذلك عاما على الإطلاق. يجب عليك فقط عرض هذا applacter.applyint و ccharacter.getint بدلا من الكائن الذي يتعامل معه. لا تعرض تنفيذك من هذا القبيل.

الحفاظ على الحقول الخاصة بك الخاصة! أنت لا ترغب أبدا في فضح الكثير من واجهة برمجة التطبيقات الخاصة بك. يمكنك دائما جعل شيء كان عاما خاصا، ولكن ليس في الاتجاه الآخر، في الإصدارات المستقبلية.

فكر كما لو أنك ستجعل ذلك في MMORPG التالي. سيكون لديك الكثير من النطاق للحشرات والأخطاء والشر غير الضروري. ضمان خصائص ثابتة نهائية.

فكر في مشغل DVD، مع واجهة Miniamilistic (Play، STOP، MENU)، ومع ذلك مثل هذه التقنية في الداخل. سترغب في إخفاء كل شيء غير حيوي في البرنامج الخاص بك.

يبدو أن شكواك الرئيسية ليست كبيرة مع تجريد أساليب SETTER / Getter، ولكن مع بناء جملة اللغة لاستخدامها. أي، كنت تفضل شيئا مثل خصائص نمط C #.

إذا كان هذا هو الحال، فإن لغة Java لديها القليل نسبيا لتقدم لك. الوصول المباشر إلى الميدان على ما يرام، حتى تحتاج إلى التبديل إلى Getter أو Setter، ثم سيكون لديك بعض إعادة الإنفاق للقيام به (ربما موافق، إذا كنت تتحكم في CodeBase بالكامل).

بالطبع، إذا كانت منصة Java متطلبات، ولكن اللغة ليست بعد ذلك هناك بدائل أخرى. يحتوي Scala على بناء جملة خاصية لطيفة للغاية، على سبيل المثال، إلى جانب الكثير من الميزات الأخرى التي قد تكون مفيدة لمثل هذا المشروع. والأفضل من ذلك كله، يتم تشغيله على JVM، لذلك لا تزال تحصل على نفس القدرة التي تحصل عليها عن طريق كتابتها في لغة Java. :)

ما يبدو أن لديك هنا هو طبقة واحدة من نموذج مركب.

قد ترغب في إضافة طرق تضيف التجريدات إلى النموذج، بدلا من مجرد وجودها كمجموعة من طرازات المستوى الأدنى.

يجب أن تكون الحقول نهائية، لذلك حتى لو كنت تجعلهم عامة، فلا يمكنك تعيينه عن طريق الخطأ null لهم.

بادئة "احصل" موجودة لجميع العناية، لذلك من المحتمل أن تكون النظرة الأولية من مشكلة جديدة مثلها.

هل هناك مزايا التغليف ضخمة للغاية لدرجة أنني يجب أن أذهب إلى الأمام ولديها Getter Getters و Setter Getters وهلم جرا ... أو يجب أن أتغلب على تدابير صحية وترك المجال كحقل عام؟

المنظمة البحرية الدولية، والتغليف لا علاقة له بتفويل A Getter / Sedter حول حقل خاص. في جرعات صغيرة، أو عند كتابة مكتبات للأغراض العامة، فإن المفاضلة مقبولة. ولكن عندما تركت لم يتم التحقق منه في نظام مثل الشخص الذي تصفه، فإنه antipattern..

المشكلة مع Getters / Setsters هي أن أنها تخلق اقتران ضيق مفرط بين الكائن مع تلك الأساليب وبقية النظام.

تتمثل إحدى مزايا التغليف الحقيقي في أنها تقلل من الحاجة إلى Getters و Setters، أو فصل كائنك من بقية النظام في هذه العملية.

بدلا من تعريض تنفيذ gamecharacter مع setintelligence، لماذا لا تعطي gamecharacter واجهة تعكس أفضل دورها في نظام اللعبة؟

على سبيل المثال، بدلا من:

// pseudo-encapsulation anti-pattern
public class GameCharacter
{
  private Intelligence intelligence;

  public Intelligence getIntelligence()
  {
    return intelligence
  }

  public void setIntelligence(Intelligence intelligence)
  {
    this.intelligence = intelligence;
  }
}

لماذا لا تجرب هذا؟:

// better encapsulation
public class GameCharacter
{
  public void grabObject(GameObject object)
  {
    // TODO update intelligence, etc.
  }

  public int getIntelligence()
  {
    // TODO
  }
}

أو حتى أفضل:

// still better
public interface GameCharacter
{
  public void grabObject(GameObject object); // might update intelligence
  public int getIntelligence();
}

public class Ogre implements GameCharacter
{
  // TODO: never increases intelligence after grabbing objects
}

في الكلمة الأخرى، يمكن gamecharacter الاستيلاء على gameobjects. تختلف تأثير كل Gamecharacter على نفس Gamebject (وينبغي) تختلف (يجب)، ولكن التفاصيل مغلفة بالكامل داخل كل تطبيق gamecharacter.

لاحظ كيف أصبح Gamecharacter الآن مسؤولا عن التعامل مع تحديث المخابرات الخاصة به (فحص النطاق، إلخ)، والتي يمكن أن تحدث أثناء الاستيلاء على GameObjects، على سبيل المثال. اختفى STER (والمضاعفات التي تلاحظ بها مع وجودها). قد تكون قادرا على الاستغناء عن طريقة Getintelligence تماما، اعتمادا على الوضع. ألن هولوب يأخذ هذه الفكرة إلى الاستنتاج المنطقي, ولكن ولكن هذا النهج لا يبدو أنه شائع جدا.

بالإضافة إلى إجابة URI (التي أدعمها تماما)، أود أن أقترح عليك التفكير في تحديد خريطة السمة في البيانات. سيؤدي ذلك إلى جعل برنامجك مرن للغاية وسيعمل على الكثير من التعليمات البرمجية التي لا تدرك أنك لا تحتاج إليها.

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

بهذه الطريقة، ليس لديك أي رمز لكل سمة لكتابة فصل إلى DB أو عرضه على الشاشة. يمكنك ببساطة التكرار فوق السمات واستخدام المعلومات المخزنة في الداخل.

نفس الشيء يمكن أن ينطبق على المهارات - في الواقع، والسمات والمهارات ربما تستمد من نفس الطبقة الأساسية.

بعد النزول من هذا الطريق، من المحتمل أن تجد نفسك بملف نصي خطير للغاية لتحديد السمات والمهارات، ولكن ستكون إضافة مهارة جديدة سهلة مثل:

Skill: Weaving
  DBName: BasketWeavingSkill
  DisplayLocation: 102, 20  #using coordinates probably isn't the best idea.
  Requires: Int=8
  Requires: Dex=12
  MaxLevel=50

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

Action: Weave Basket
  text: You attempt to weave a basket from straw
  materials: Straw
  case Weaving < 1
    test: You don't have the skill!
  case Weaving < 10
    text: You make a lame basket
    subtract 10 straw
    create basket value 8
    improve skill weaving 1%
  case Weaving < 40
    text: You make a decent basket
    subtract 10 straw
    create basket value 30
    improve skill weaving 0.1%
  case Weaving < 50
    text: You make an awesome basket!
    subtract 10 straw
    create basket value 100
    improve skill weaving 0.01%
  case Weaving = 50
    text: OMG, you made the basket of the gods!
    subtract 10 straw
    create basket value 1000

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

فكر في استخدام إطار "النمذجة"، مثل Eclipse EMF. ينطوي هذا على تحديد الكائنات الخاصة بك باستخدام شيء مثل Eclipse EMF Editor، أو في XML عادي، أو في وردة عقلانية. انها ليست صعبة كما يبدو. يمكنك تحديد السمات والقيود وما إلى ذلك. ثم يولد الإطار التعليمات البرمجية لك. يضيف Tationsgenerated العلامات إلى أجزاء تضاف مثل أساليب Getter و Setter. يمكنك تخصيص التعليمات البرمجية، وحررها لاحقا إما باليد أو عبر بعض واجهة المستخدم الرسومية، وتجديد ملفات Java.

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