هي betters و setters تصميم ضعيف؟ نصيحة متناقضة شوهدت [مكررة

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

سؤال

هذا السؤال لديه بالفعل إجابة هنا:

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

بعد إلقاء نظرة سريعة على التعليمات البرمجية، كان غالبية ذلك Getters و Setters (60٪) مقارنة بالباقي اللازم حقا لمنطق اللعبة.

زعمت بضعة عمليات بحث Google أن Getters and Stationers شريرة، بينما ادعى آخرون أنهم ضروريون لممارسة OO جيدة وبرامج رائعة.

اذا ماذا يجب أن أفعل؟ الذي يجب أن يكون؟ يجب أن أغير My Getters و Setters لمتغيراتي الخاصة، أو يجب أن ألتصق بها؟

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

المحلول

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

// Game
private int score;
public void setScore(int score) { this.score = score; }
public int getScore() { return score; }
// Usage
game.setScore(game.getScore() + ENEMY_DESTROYED_SCORE);

يجب أن يكون

// Game
private int score;
public int getScore() { return score; }
public void addScore(int delta) { score += delta; }
// Usage
game.addScore(ENEMY_DESTROYED_SCORE);

ربما هذا ربما بعض الأمثلة على الوجه. ما أحاول قوله هو أن مناقشة Getter / Setter مقابل الحقول العامة غالبا ما يحجب مشاكل أكبر مع الكائنات التي تلمع الحالة الداخلية لبعضها البعض بطريقة حميمة وبالتالي كونها مقترنة عن كثب.

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

private boolean alive = true;
public boolean isAlive() { return alive; }
public void kill() { alive = false; }

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

private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }

نصائح أخرى

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

هذا يعتمد حقا على الوضع رغم ذلك - في بعض الأحيان أنت حقا فعل فقط تريد كائن بيانات غبية.

لقد كان لديك بالفعل الكثير من الإجابات الجيدة على هذا، لذلك سوف أعطي فقط سنتين. getters و setters هي شريرة جدا جدا. إنهم يتيحون لك بشكل أساسي أن يخفيوا تناول كائناتك عندما يتم إلقاء معظم الوقت الذي قمت به كل ما قمت به في رمز زائد لا يفعل شيئا لإخفاء الحالة الداخلية. للحصول على POJO بسيطة، لا يوجد سبب يجعل GetName () و Setname () لا يمكن استبداله ب OBJ.NAME = "TOM".

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

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

getters و setters فرض مفهوم التغليف في البرمجة الموجهة للكائنات.

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

هناك عدد قليل من المزايا للحصول على getters و setters:

1. السماح بالتغييرات المستقبلية دون تعديل إلى التعليمات البرمجية التي تستخدم الفئة المعدلة.

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

على سبيل المثال، دعنا نقول أن هناك setValue الطريقة التي تحدد value متغير خاص في كائن:

public void setValue(int value)
{
    this.value = value;
}

ولكن بعد ذلك، كان هناك شرط جديد يحتاج إلى تتبع عدد المرات value تم تغيير. مع Seadter في مكانه، التغيير تافه إلى حد ما:

public void setValue(int value)
{
    this.value = value;
    count++;
}

إذا كان value كان الحقل عام، لا توجد طريقة سهلة للعودة لاحقا وإضافة عداد يحتفظ بعدد مرات تم تغيير القيمة. لذلك، فإن وجود getters و setters هي طريقة واحدة إلى "مقاومة المستقبل" للفئة للتغييرات التي قد تأتي في وقت لاحق.

2. إنفاذ الوسائل التي يمكن به التلاعب بها الكائن.

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

على سبيل المثال، ImmutableArray يحتوي الكائن على an. int مجموعة دعا myArray. وبعد إذا كانت الصفيف مجالا عاما، فلن يكون ذلك غير قابل للتغيير:

ImmutableArray a = new ImmutableArray();
int[] b = a.myArray;
b[0] = 10;      // Oops, the ImmutableArray a's contents have been changed.

لتنفيذ مجموعة غير قابلة للتمويل حقا، وهي شركة للحصول على الصفيف (getArray الطريقة) يجب أن تكون مكتوبة بحيث ترجع نسخة من صفيفها:

public int[] getArray()
{
    return myArray.clone();
}

وحتى إذا حدث ما يلي:

ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // No problem, only the copy of the array is affected.

ال ImmutableArray هو في الواقع ثابت. إن تعريض متغيرات كائن سيسمح له بالتلاعب بطرق غير مخصصة، ولكن فقط تعريض طرق معينة (Getters and Setters)، يمكن معالجة الكائن في الطرق المقصودة.

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

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

انهم شرير تماما.

accoobird لسوء الحظ انهم لا يفعلون تماما "فرض مفهوم التغليف"، كل ما يفعلونه هو جعلك تعتقد أنك تتغلف من البيانات عند اكتشاف البيانات عبر عقار مع أوهام طريقة Grandeur. أي شيء يوجد Getter / Sedter يقوم حقل عام بشكل أفضل.

أولا، إذا كنت تريد بيانات عامة، اجعلها عامة، والتخلص من أساليب Getter & Setter لتقليل عدد الأساليب التي يتعين على العميل.

object.field = value;

بدلا من المكثف أكثر مصرفية

object.setField(value);

حيث يجب على العميل الآن التحقق من طريقة Getter / Setter لمعرفة ما إذا كان لديه أي آثار جانبية.

ثانيا، إذا كنت بحاجة فعلا إلى القيام بشيء آخر في الطريقة، فلماذا نسميها طريقة الحصول على / تعيين عندما يكون لديك المزيد من المسؤوليات أكثر من مجرد الحصول على أو الإعداد؟ إما اتبع SRP أو اتصل بالطريقة التي يخبرك بها بالفعل ما كامل الطريقة التي تعجبها أمثلة زاركونن التي ذكرها على سبيل المثال.

public void kill(){
    isAlive = false;
    removeFromWorld(this);
}

بدلا من

public void setAlive(boolean isAlive){
    this.isAlive = isAlive;
    if (isAlive)
        addToWorld(this);
    else
        removeFromWorld(this);
}

من أين تخبر طريقة Setalive (Boolean) العميل أنه كإعداد جانبي، فسيقوم بإزالة الكائن من العالم؟ لماذا يجب أن يكون لدى العميل أي معرفة حول حقل Isalive؟ بالإضافة إلى ما يحدث عندما يتم إعادة إضافة الكائن إلى العالم، هل يجب إعادة تهيئته؟ لماذا يهتم العميل بأي من ذلك؟

IMHO الأخلاقي هو تسمية طرق القول بالضبط ما يفعلونه، اتبع SRP والتخلص من Getters / Setters. إذا كانت هناك مشاكل بدون Getters / setters، فحول الأشياء للقيام بعملهم القذر في فئتها بدلا من محاولة القيام بأشياء معهم في فصول أخرى.

هنا يصدق بلدي rant، آسف لذلك؛)

إنه منحدر زلق.

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

هناك أيضا حالة فئة تعرض بعض "المقابض السيطرة"؛ ربما يمكن فهم واجهة المستخدم راديو سيارتك على أنها تعريض شيء مثل getVolume, setVolume, getChannel, ، و setChannel, ، لكن وظائفها الحقيقية تتلقى إشارات وانبعاثات الصوت. لكن تلك المقابض لا تعرض تفاصيل التنفيذ كثيرا؛ أنت لا تعرف من تلك الواجهة ميزات ما إذا كان الراديو هو الترانزستورات، معظمها - برامج، أو أنابيب فراغ.

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

شرير جدا"؟ ليس صحيحا. ولكن في كل مرة يميل فيها إلى وضع قيمة وفضح get... و set... الأساليب على هذه القيمة، اسأل نفسك عن السبب، وما هو مجلد هذا الكائن هو حقا. إذا كانت الإجابة الوحيدة التي يمكنك تقديمها لنفسك، "لعقد هذه القيمة بالنسبة لي"، ثم يمكن شيء إلى جانب OO يحدث هنا.

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

يحرر: إذا كانت النقطة الرئيسية للمحتجزين والخادمين هي "تكوين" Classe (أفهم تعليقك بهذه الطريقة)، فمن المحتمل أنك لا تحتاج إلى Getters (فهي بخير تماما لفئة للوصول إلى المتغيرات الخاصة بها دون استخدام احصل على طرق)، وربما يمكنك طي العديد من Setters في "Setters Group" تعيين العديد من المتغيرات التي تنتمي معا من الناحية الفنية.

يميل وجود Getter والشريط إلى الإشارة إلى ("رائحة" إذا كنت في هذا النوع من اللغة التعليمية الابتدائية) أن هناك مشكلة في التصميم. بالكاد يمكن تمييز Jetters و Setters من المجالات العامة. عادة ما تكون التعليمات البرمجية التي تعمل على البيانات في مغلفة ضعيفة فئة مختلفة، وما تتوقعه من المبرمجين غير مرتاحين مع OO.

في بعض الحالات Getters و Setters بخير. ولكن كقاعدة عامة، تشير إلى كل من Getters و Setters على مشاكل التصميم. getters العمل من أجل عدم القدرة؛ يعمل التعامل مع "معرفة لا تسأل". كل من التحمل و "لا تسأل لا تسأل" خيارات تصميم جيدة، طالما لم يتم تطبيقها في نمط متداخل.

رأيي هو أن Getters و Setters هي شرط للحصول على برامج جيدة. العصا معهم، ولكن لا تكتب getters / setters غير ضرورية - ليس من الضروري دائما التعامل مباشرة مع جميع المتغيرات مباشرة.

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

مثال واحد قرأته أعلاه كان future-proofing الكود الخاص بك. علي سبيل المثال:

public void setValue(int value)
{
    this.value = value;
}

ثم، تتغير المتطلبات وتحتاج إلى تتبع عدد المرات التي تم تعيين القيمة.

وبالتالي:

public void setValue(int value)
{
    this.value = value;
    count++;
}

هذا جميل. فهمت. ومع ذلك، في روبي، سوف لا يخدم ما يلي الغرض نفسه؟

someobject.my_value = 100

في وقت لاحق، تحتاج إلى تتبع عدد المرات my_value تم تعيين. حسنا، هل لا يمكنك تجاوز STERTER ومن بعد و فقط ومن بعد?

def my_value=(value)
    @my_value = value
    @count++
end

أنا جميعا على الكود الجميل ولكن يجب أن أعترف، والنظر عبر جبال فئات جافا لدينا ورؤية حرفيا الآلاف والآلاف من خطوط التعليمات البرمجية التي لا شيء سوى getter / setter الأساسي قبيحة ومزعجة.

عندما كنت أتطور في الوقت الكامل، استخدمنا الخصائص العامة في كل وقت وقمنا بتجزئة مخصصة للأطفال فقط عند الحاجة. عملت مثل سحر ولم يكسر أي شيء.

كما هو الحال دائما الإجابة الوحيدة هي: ذلك يعتمد. إذا كنت اليرون الوحيد الذي يلمس الكود، فيمكنك فعل أي شيء تشعر بالراحة فيه، بما في ذلك إجراء اختصارات.

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

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

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

في المقابل، في عالم Python World، يعتبر عموما نمطا سيئا: يضيفون خطوطا إلى التعليمات البرمجية دون إضافة وظيفة. عندما يحتاج مبرمجات Python، يمكنهم استخدام metaprogramming للقبض على الحصول على وسمات الكائن و / أو وضعها.

في جاوة (على الأقل نسخة من جافا تعلمت قليلا قبل عقد من الزمن)، لم يكن ذلك ممكنا. وبالتالي، في Java، عادة ما يكون من الأفضل استخدام Getters و Setters دينيا، بحيث إذا كنت بحاجة إلى ذلك، يمكنك تجاوز الوصول إلى المتغيرات.

(هذا لا يجعل بيثون أفضل بالضرورة من جافا، مختلفة تماما.)

فقط FYI: بالإضافة إلى جميع الإجابات الممتازة في هذا الموضوع، تذكر أن كل الأسباب التي يمكنك التوصل إليها مع أو ضد getters / setters، الأداء ليس واحدا (كما قد يعتقد البعض). JVM ذكي بما فيه الكفاية لإمكان Getters / setters تافهة (حتى غيرfinal تلك، طالما أنها ليست في الواقع مجلد).

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

إذا كنت بحاجة إلى الوصول الخارجي إلى القيم الفردية للحقول، فاستخدم Getters و / أو setters. إذا لم يكن كذلك، لا. لا تستخدم الحقول العامة. إنها بهذه السهولة! (حسنا، ليس أبدا الذي - التي بسيطة، لكنها قاعدة جيدة للإبهام).

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

لقد كنت برمجة في جافا لبضع مونتس قبل، وقد تعلمت أننا يجب أن نستخدم getters & setters فقط عندما يكون ذلك ضروريا للتطبيق

استمتع :)

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