سؤال

من حيث جافا، عندما يسأل شخص ما:

ما هو تعدد الأشكال؟

كان الحمولة الزائدة أو تجاوز تكون إجابة مقبولة؟

أعتقد أن هناك ما هو أكثر قليلاً من ذلك.

إذا كان لديك فئة أساسية مجردة تحدد طريقة بدون تطبيق، وقمت بتعريف هذه الطريقة في الفئة الفرعية، فهل لا يزال هذا الأمر مهيمنًا؟

أظن الحمولة الزائدة ليست الإجابة الصحيحة بالتأكيد.

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

المحلول

إن أوضح طريقة للتعبير عن تعدد الأشكال هي عبر فئة أساسية مجردة (أو واجهة)

public abstract class Human{
   ...
   public abstract void goPee();
}

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

لذلك نقوم بتأجيل التنفيذ باستخدام فئة مجردة.

public class Male extends Human{
...
    @Override
    public void goPee(){
        System.out.println("Stand Up");
    }
}

و

public class Female extends Human{
...
    @Override
    public void goPee(){
        System.out.println("Sit Down");
    }
}

الآن يمكننا أن نطلب من غرفة كاملة مليئة بالبشر أن يتبولوا.

public static void main(String[] args){
    ArrayList<Human> group = new ArrayList<Human>();
    group.add(new Male());
    group.add(new Female());
    // ... add more...

    // tell the class to take a pee break
    for (Human person : group) person.goPee();
}

تشغيل هذا من شأنه أن يؤدي إلى:

Stand Up
Sit Down
...

نصائح أخرى

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

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

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

فيما يلي مثال على تعدد الأشكال في pseudo-C#/Java:

class Animal
{
    abstract string MakeNoise ();
}

class Cat : Animal {
    string MakeNoise () {
        return "Meow";
    }
}

class Dog : Animal {
    string MakeNoise () {
        return "Bark";
    }
}

Main () {
   Animal animal = Zoo.GetAnimal ();
   Console.WriteLine (animal.MakeNoise ());
}

لا تعرف الوظيفة الرئيسية نوع الحيوان وتعتمد على سلوك تنفيذ معين لطريقة MakeNoise().

يحرر:يبدو أن براين ضربني بلكمة.من المضحك أننا استخدمنا نفس المثال.لكن الكود أعلاه يجب أن يساعد في توضيح المفاهيم.

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

يمكن تحقيق تعدد الأشكال باستخدام طريقتين، هما

  1. تجاوز الأسلوب
  2. طريقة التحميل الزائد

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

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

في Java لتحقيق تعدد الأشكال، يمكن للمتغير المرجعي للفئة الفائقة الاحتفاظ بكائن الفئة الفرعية.

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

يتم استخدام كل من التجاوز والتحميل الزائد لتحقيق تعدد الأشكال.

You could have a method in a class that is تم تجاوزه in one or more subclasses. The method does different things depending on which class was used to instantiate an object.

    abstract class Beverage {
       boolean isAcceptableTemperature();
    }

    class Coffee extends Beverage {
       boolean isAcceptableTemperature() { 
           return temperature > 70;
       }
    }

    class Wine extends Beverage {
       boolean isAcceptableTemperature() { 
           return temperature < 10;
       }
    }

يمكن أن يكون لديك أيضًا طريقة مثقلة مع مجموعتين أو أكثر من الحجج.

    class Server {
        public void pour (Coffee liquid) {
            new Cup().fillToTopWith(liquid);
        }

        public void pour (Wine liquid) {
            new WineGlass().fillHalfwayWith(liquid);
        }

        public void pour (Lemonade liquid, boolean ice) {
            Glass glass = new Glass();
            if (ice) {
                glass.fillToTopWith(new Ice());
            }
            glass.fillToTopWith(liquid);
        }
    }

أنت على حق في أن التحميل الزائد ليس هو الحل.

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

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

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

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

تعدد الأشكال يعني ببساطة "أشكال عديدة".

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

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

لذا، بما أن الواجهات تصف السلوك، وأسماء الطرق تصف السلوك (للمبرمج)، فليس من المبالغة اعتبار التحميل الزائد للطريقة شكلاً أقل من تعدد الأشكال.

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

ليس من الضروري أن يعرف رمز الاتصال نوع الحيوان المحدد.

هذا ما أعتقد أنه تعدد الأشكال.

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

لا:

يحدث التحميل الزائد عندما يكون لديك نفس اسم الوظيفة الذي يأخذ معلمات مختلفة.

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

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

في Java ترى تعدد الأشكال كثيرًا في مكتبة المجموعات:

int countStuff(List stuff) {
  return stuff.size();
}

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

List myStuff = new MyTotallyAwesomeList();
int result = countStuff(myStuff);

إذا كنت تفرط في التحميل سيكون لديك:

int countStuff(LinkedList stuff) {...}
int countStuff(ArrayList stuff) {...}
int countStuff(MyTotallyAwesomeList stuff) {...}
etc...

وسيتم اختيار الإصدار الصحيح من countStuff() بواسطة المترجم لمطابقة المعلمات.

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

public int DoSomething(int objectId) { ... }
public int DoSomething(string objectName) { ... }

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

يشير التجاوز عادة إلى تعدد الأشكال، كما وصفته في سؤالك

التحميل الزائد هو عندما تحدد طريقتين بنفس الاسم ولكن بمعلمات مختلفة

التجاوز هو المكان الذي تقوم فيه بتغيير سلوك الفئة الأساسية عبر وظيفة بنفس الاسم في فئة فرعية.

لذا فإن تعدد الأشكال يرتبط بالتجاوز ولكن ليس التحميل الزائد حقًا.

However if someone gave me a simple answer of "overriding" for the question "What is polymorphism?" I would ask for further explanation.

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

ما هو تعدد الأشكال؟

من جافا درس تعليمي

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

من خلال النظر في الأمثلة والتعريف ، تجاوز يجب أن تكون الإجابة مقبولة.

بخصوص استفسارك الثاني:

إذا كان لديك فئة أساسية مجردة تحدد طريقة بدون تطبيق، وقمت بتعريف هذه الطريقة في الفئة الفرعية، فهل لا يزال هذا الأمر مهيمنًا؟

ينبغي أن يسمى التجاوز.

ألقِ نظرة على هذا المثال لفهم الأنواع المختلفة من التجاوز.

  1. لا توفر الفئة الأساسية أي تطبيق ويجب على الفئة الفرعية تجاوز الطريقة الكاملة - (ملخص)
  2. توفر الفئة الأساسية التنفيذ الافتراضي ويمكن للفئة الفرعية تغيير السلوك
  3. تضيف الفئة الفرعية امتدادًا لتطبيق الفئة الأساسية عن طريق الاتصال super.methodName() كأول بيان
  4. تحدد الفئة الأساسية بنية الخوارزمية (طريقة القالب) وستتجاوز الفئة الفرعية جزءًا من الخوارزمية

مقتطف الكود:

import java.util.HashMap;

abstract class Game implements Runnable{

    protected boolean runGame = true;
    protected Player player1 = null;
    protected Player player2 = null;
    protected Player currentPlayer = null;

    public Game(){
        player1 = new Player("Player 1");
        player2 = new Player("Player 2");
        currentPlayer = player1;
        initializeGame();
    }

    /* Type 1: Let subclass define own implementation. Base class defines abstract method to force
        sub-classes to define implementation    
    */

    protected abstract void initializeGame();

    /* Type 2: Sub-class can change the behaviour. If not, base class behaviour is applicable */
    protected void logTimeBetweenMoves(Player player){
        System.out.println("Base class: Move Duration: player.PlayerActTime - player.MoveShownTime");
    }

    /* Type 3: Base class provides implementation. Sub-class can enhance base class implementation by calling
        super.methodName() in first line of the child class method and specific implementation later */
    protected void logGameStatistics(){
        System.out.println("Base class: logGameStatistics:");
    }
    /* Type 4: Template method: Structure of base class can't be changed but sub-class can some part of behaviour */
    protected void runGame() throws Exception{
        System.out.println("Base class: Defining the flow for Game:");  
        while ( runGame) {
            /*
            1. Set current player
            2. Get Player Move
            */
            validatePlayerMove(currentPlayer);  
            logTimeBetweenMoves(currentPlayer);
            Thread.sleep(500);
            setNextPlayer();
        }
        logGameStatistics();
    }
    /* sub-part of the template method, which define child class behaviour */
    protected abstract void validatePlayerMove(Player p);

    protected void setRunGame(boolean status){
        this.runGame = status;
    }
    public void setCurrentPlayer(Player p){
        this.currentPlayer = p;
    }
    public void setNextPlayer(){
        if ( currentPlayer == player1) {
            currentPlayer = player2;
        }else{
            currentPlayer = player1;
        }
    }
    public void run(){
        try{
            runGame();
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

class Player{
    String name;
    Player(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}

/* Concrete Game implementation  */
class Chess extends Game{
    public Chess(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized Chess game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate Chess move:"+p.getName());
    }
    protected void logGameStatistics(){
        super.logGameStatistics();
        System.out.println("Child class: Add Chess specific logGameStatistics:");
    }
}
class TicTacToe extends Game{
    public TicTacToe(){
        super();
    }
    public void initializeGame(){
        System.out.println("Child class: Initialized TicTacToe game");
    }
    protected void validatePlayerMove(Player p){
        System.out.println("Child class: Validate TicTacToe move:"+p.getName());
    }
}

public class Polymorphism{
    public static void main(String args[]){
        try{

            Game game = new Chess();
            Thread t1 = new Thread(game);
            t1.start();
            Thread.sleep(1000);
            game.setRunGame(false);
            Thread.sleep(1000);

            game = new TicTacToe();
            Thread t2 = new Thread(game);
            t2.start();
            Thread.sleep(1000);
            game.setRunGame(false);

        }catch(Exception err){
            err.printStackTrace();
        }       
    }
}

انتاج:

Child class: Initialized Chess game
Base class: Defining the flow for Game:
Child class: Validate Chess move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate Chess move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:
Child class: Add Chess specific logGameStatistics:
Child class: Initialized TicTacToe game
Base class: Defining the flow for Game:
Child class: Validate TicTacToe move:Player 1
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Child class: Validate TicTacToe move:Player 2
Base class: Move Duration: player.PlayerActTime - player.MoveShownTime
Base class: logGameStatistics:

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

  1. ملزمة متأخرة
  2. ميراث.

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

ولكن في C++ الأمر ليس كذلك.أي مثقلة الطريقة، بشكل مستقل سواء كان التوقيع هو نفسه أم لا (كمية مختلفة، نوع مختلف) كذلك تم تجاوزه.وهذا يعني أن طريقة الفئة الأساسية لم تعد متاحة في الفئة الفرعية حتى يومنا هذا عند استدعائها من خارج كائن الفئة الفرعية.

لذا فإن الجواب هو عند الحديث عن استخدام جافا الحمولة الزائدة.في أي لغة أخرى قد يكون الأمر مختلفًا كما يحدث في لغة C++

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

لماذا يعد تعدد الأشكال مهمًا جدًا في أي لغة OOP؟

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

لنفترض أنك مهندس برمجيات في شركة تلفزيون وطُلب منك كتابة برنامج لوحدات التحكم في الحجم والسطوع والألوان لزيادة قيمها وتقليلها بناءً على أمر المستخدم.

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

  1. set: - لتعيين قيمة وحدة التحكم. (بافتراض أن هذا يحتوي على رمز خاص بوحدة التحكم)
  2. الحصول على: - للحصول على قيمة وحدة التحكم. (بافتراض أن هذا يحتوي على رمز خاص بوحدة التحكم)
  3. ضبط: - للتحقق من صحة الإدخال وإعداد وحدة التحكم. (عمليات التحقق العامة..مستقلة عن وحدات التحكم)
  4. تعيين إدخال المستخدم باستخدام وحدات التحكم: - للحصول على مدخلات المستخدم واستدعاء وحدات التحكم وفقًا لذلك.

إصدار التطبيق 1

import java.util.Scanner;    
class VolumeControllerV1 {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class  BrightnessControllerV1 {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class ColourControllerV1    {
    private int value;
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

/*
 *       There can be n number of controllers
 * */
public class TvApplicationV1 {
    public static void main(String[] args)  {
        VolumeControllerV1 volumeControllerV1 = new VolumeControllerV1();
        BrightnessControllerV1 brightnessControllerV1 = new BrightnessControllerV1();
        ColourControllerV1 colourControllerV1 = new ColourControllerV1();


        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println("Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV1.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV1.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV1.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV1.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV1.adjust(5);
                    break;
                }
                case 6: {
                colourControllerV1.adjust(-5);
                break;
            }
            default:
                System.out.println("Shutting down...........");
                break OUTER;
        }

    }
    }
}

الآن لديك الإصدار الأول من تطبيق العمل جاهز للنشر.حان الوقت لتحليل العمل المنجز حتى الآن.

مشاكل في إصدار تطبيق التلفزيون 1

  1. رمز الضبط (قيمة int) مكرر في الفئات الثلاثة.ترغب في تقليل ازدواجية التعليمات البرمجية.(لكنك لم تفكر في الكود الشائع ونقله إلى فئة فائقة لتجنب تكرار الكود)

عليك أن تقرر التعايش مع ذلك طالما أن تطبيقك يعمل كما هو متوقع.

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

تبدأ في كتابة فئة جديدة (ResetFunctionV2) للوظيفة الجديدة وتعيين رمز تعيين إدخال المستخدم لهذه الميزة الجديدة.

إصدار التطبيق 2

import java.util.Scanner;
class VolumeControllerV2    {

    private int defaultValue = 25;
    private int value;

    int getDefaultValue() {
        return defaultValue;
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class  BrightnessControllerV2   {

    private int defaultValue = 50;
    private int value;
    int get()    {
        return value;
    }
    int getDefaultValue() {
        return defaultValue;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}
class ColourControllerV2    {

    private int defaultValue = 40;
    private int value;
    int get()    {
        return value;
    }
    int getDefaultValue() {
        return defaultValue;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

class ResetFunctionV2 {

    private VolumeControllerV2 volumeControllerV2 ;
    private BrightnessControllerV2 brightnessControllerV2;
    private ColourControllerV2 colourControllerV2;

    ResetFunctionV2(VolumeControllerV2 volumeControllerV2, BrightnessControllerV2 brightnessControllerV2, ColourControllerV2 colourControllerV2)  {
        this.volumeControllerV2 = volumeControllerV2;
        this.brightnessControllerV2 = brightnessControllerV2;
        this.colourControllerV2 = colourControllerV2;
    }
    void onReset()    {
        volumeControllerV2.set(volumeControllerV2.getDefaultValue());
        brightnessControllerV2.set(brightnessControllerV2.getDefaultValue());
        colourControllerV2.set(colourControllerV2.getDefaultValue());
    }
}
/*
 *       so on
 *       There can be n number of controllers
 *
 * */
public class TvApplicationV2 {
    public static void main(String[] args)  {
        VolumeControllerV2 volumeControllerV2 = new VolumeControllerV2();
        BrightnessControllerV2 brightnessControllerV2 = new BrightnessControllerV2();
        ColourControllerV2 colourControllerV2 = new ColourControllerV2();

        ResetFunctionV2 resetFunctionV2 = new ResetFunctionV2(volumeControllerV2, brightnessControllerV2, colourControllerV2);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV2.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV2.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV2.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV2.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV2.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV2.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV2.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

وبذلك يكون تطبيقك جاهزًا باستخدام ميزة إعادة الضبط.لكن الآن بدأت تدرك ذلك

مشاكل في إصدار تطبيق التلفزيون 2

  1. إذا تم تقديم وحدة تحكم جديدة للمنتج، فيجب عليك تغيير رمز ميزة إعادة التعيين.
  2. إذا زاد عدد وحدات التحكم بشكل كبير، فستواجه مشكلة في الاحتفاظ بمراجع وحدات التحكم.
  3. يقترن رمز ميزة إعادة الضبط بإحكام بجميع رموز فئة وحدات التحكم (للحصول على القيم الافتراضية وتعيينها).
  4. يمكن لفئة ميزة إعادة الضبط (ResetFunctionV2) الوصول إلى طريقة أخرى لفئة وحدة التحكم (الضبط) وهو أمر غير مرغوب فيه.

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

الآن بدأت بالتفكير في أن هذه الميزة الجديدة التي ستتم إضافتها تشبه ميزة إعادة الضبط وسيتم مضاعفة مشكلات التطبيق (V2) إذا لم تقم بإعادة معالجة تطبيقك.

تبدأ بالتفكير في استخدام الوراثة بحيث يمكنك الاستفادة من القدرة المتعددة الأشكال لـ JAVA وإضافة فئة مجردة جديدة (ControllerV3) إلى

  1. أعلن عن توقيع طريقة get and set.
  2. تحتوي على تطبيق أسلوب الضبط الذي تم تكراره مسبقًا بين جميع وحدات التحكم.
  3. قم بتعريف طريقة setDefault بحيث يمكن تنفيذ ميزة إعادة التعيين بسهولة من خلال الاستفادة من تعدد الأشكال.

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

إصدار التطبيق 3

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

abstract class ControllerV3 {
    abstract void set(int value);
    abstract int get();
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
    abstract void setDefault();
}
class VolumeControllerV3 extends ControllerV3   {

    private int defaultValue = 25;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
}
class  BrightnessControllerV3  extends ControllerV3   {

    private int defaultValue = 50;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }
}
class ColourControllerV3 extends ControllerV3   {

    private int defaultValue = 40;
    private int value;

    public void setDefault() {
        set(defaultValue);
    }
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
}

class ResetFunctionV3 {

    private List<ControllerV3> controllers = null;

    ResetFunctionV3(List<ControllerV3> controllers)  {
        this.controllers = controllers;
    }
    void onReset()    {
        for (ControllerV3 controllerV3 :this.controllers)  {
            controllerV3.setDefault();
        }
    }
}
/*
 *       so on
 *       There can be n number of controllers
 *
 * */
public class TvApplicationV3 {
    public static void main(String[] args)  {
        VolumeControllerV3 volumeControllerV3 = new VolumeControllerV3();
        BrightnessControllerV3 brightnessControllerV3 = new BrightnessControllerV3();
        ColourControllerV3 colourControllerV3 = new ColourControllerV3();

        List<ControllerV3> controllerV3s = new ArrayList<>();
        controllerV3s.add(volumeControllerV3);
        controllerV3s.add(brightnessControllerV3);
        controllerV3s.add(colourControllerV3);

        ResetFunctionV3 resetFunctionV3 = new ResetFunctionV3(controllerV3s);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV3.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV3.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV3.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV3.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV3.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV3.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV3.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

على الرغم من أن معظم المشكلات المدرجة في قائمة المشكلات الخاصة بالإصدار V2 تمت معالجتها باستثناء

مشاكل في تطبيق TV الإصدار 3

  1. يمكن لفئة ميزة إعادة الضبط (ResetFunctionV3) الوصول إلى طريقة أخرى لفئة وحدة التحكم (الضبط) وهو أمر غير مرغوب فيه.

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

لذلك تقوم بتقسيم العقد المحدد في فئة مجردة وكتابة واجهتين لهما

  1. ميزة إعادة الضبط.
  2. تحديث برنامج التشغيل.

واطلب من فصلك الخرساني الأول تنفيذها على النحو التالي

إصدار التطبيق 4

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

interface OnReset {
    void setDefault();
}
interface OnStart {
    void checkForDriverUpdate();
}
abstract class ControllerV4 implements OnReset,OnStart {
    abstract void set(int value);
    abstract int get();
    void adjust(int value)  {
        int temp = this.get();
        if(((value > 0) && (temp >= 100)) || ((value < 0) && (temp <= 0)))    {
            System.out.println("Can not adjust any further");
            return;
        }
        this.set(temp + value);
    }
}

class VolumeControllerV4 extends ControllerV4 {

    private int defaultValue = 25;
    private int value;
    @Override
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of VolumeController \t"+this.value);
        this.value = value;
        System.out.println("New value of VolumeController \t"+this.value);
    }
    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for VolumeController .... Done");
    }
}
class  BrightnessControllerV4 extends ControllerV4 {

    private int defaultValue = 50;
    private int value;
    @Override
    int get()    {
        return value;
    }
    @Override
    void set(int value) {
        System.out.println("Old value of BrightnessController \t"+this.value);
        this.value = value;
        System.out.println("New value of BrightnessController \t"+this.value);
    }

    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for BrightnessController .... Done");
    }
}
class ColourControllerV4 extends ControllerV4 {

    private int defaultValue = 40;
    private int value;
    @Override
    int get()    {
        return value;
    }
    void set(int value) {
        System.out.println("Old value of ColourController \t"+this.value);
        this.value = value;
        System.out.println("New value of ColourController \t"+this.value);
    }
    @Override
    public void setDefault() {
        set(defaultValue);
    }

    @Override
    public void checkForDriverUpdate()    {
        System.out.println("Checking driver update for ColourController .... Done");
    }
}
class ResetFunctionV4 {

    private List<OnReset> controllers = null;

    ResetFunctionV4(List<OnReset> controllers)  {
        this.controllers = controllers;
    }
    void onReset()    {
        for (OnReset onreset :this.controllers)  {
            onreset.setDefault();
        }
    }
}
class InitializeDeviceV4 {

    private List<OnStart> controllers = null;

    InitializeDeviceV4(List<OnStart> controllers)  {
        this.controllers = controllers;
    }
    void initialize()    {
        for (OnStart onStart :this.controllers)  {
            onStart.checkForDriverUpdate();
        }
    }
}
/*
*       so on
*       There can be n number of controllers
*
* */
public class TvApplicationV4 {
    public static void main(String[] args)  {
        VolumeControllerV4 volumeControllerV4 = new VolumeControllerV4();
        BrightnessControllerV4 brightnessControllerV4 = new BrightnessControllerV4();
        ColourControllerV4 colourControllerV4 = new ColourControllerV4();
        List<ControllerV4> controllerV4s = new ArrayList<>();
        controllerV4s.add(brightnessControllerV4);
        controllerV4s.add(volumeControllerV4);
        controllerV4s.add(colourControllerV4);

        List<OnStart> controllersToInitialize = new ArrayList<>();
        controllersToInitialize.addAll(controllerV4s);
        InitializeDeviceV4 initializeDeviceV4 = new InitializeDeviceV4(controllersToInitialize);
        initializeDeviceV4.initialize();

        List<OnReset> controllersToReset = new ArrayList<>();
        controllersToReset.addAll(controllerV4s);
        ResetFunctionV4 resetFunctionV4 = new ResetFunctionV4(controllersToReset);

        OUTER: while(true) {
            Scanner sc=new Scanner(System.in);
            System.out.println(" Enter your option \n Press 1 to increase volume \n Press 2 to decrease volume");
            System.out.println(" Press 3 to increase brightness \n Press 4 to decrease brightness");
            System.out.println(" Press 5 to increase color \n Press 6 to decrease color");
            System.out.println(" Press 7 to reset TV \n Press any other Button to shutdown");
            int button = sc.nextInt();
            switch (button) {
                case  1:    {
                    volumeControllerV4.adjust(5);
                    break;
                }
                case 2: {
                    volumeControllerV4.adjust(-5);
                    break;
                }
                case  3:    {
                    brightnessControllerV4.adjust(5);
                    break;
                }
                case 4: {
                    brightnessControllerV4.adjust(-5);
                    break;
                }
                case  5:    {
                    colourControllerV4.adjust(5);
                    break;
                }
                case 6: {
                    colourControllerV4.adjust(-5);
                    break;
                }
                case 7: {
                    resetFunctionV4.onReset();
                    break;
                }
                default:
                    System.out.println("Shutting down...........");
                    break OUTER;
            }

        }
    }
}

الآن تمت معالجة جميع المشكلات التي واجهتها وأدركت أنه باستخدام الوراثة وتعدد الأشكال يمكنك

  1. احتفظ بأجزاء مختلفة من التطبيق مقترنة بشكل غير محكم. (لا تحتاج مكونات ميزة إعادة الضبط أو تحديث برنامج التشغيل إلى أن تكون على دراية بفئات وحدة التحكم الفعلية (الحجم والسطوع واللون)، وأي فئة تطبق OnReset أو OnStart ستكون مقبولة لميزة إعادة الضبط أو تحديث برنامج التشغيل المكونات على التوالي).
  2. أصبح تحسين التطبيق أسهل. (لن تؤثر الإضافة الجديدة لوحدة التحكم على إعادة تعيين مكون ميزة إعادة التعيين أو تحديث برنامج التشغيل، وأصبح من السهل عليك الآن إضافة وحدات جديدة)
  3. احتفظ بطبقة من التجريد. (الآن يمكن لميزة إعادة الضبط رؤية طريقة setDefault لوحدات التحكم فقط ويمكن لميزة إعادة الضبط رؤية طريقة checkForDriverUpdate لوحدات التحكم فقط)

أتمنى أن يساعدك هذا :-)

تعدد الأشكال هو الأرجح بقدر ما هو عليه معنى تشعر بالقلق ...للتجاوز في Java

الأمر كله يتعلق بالسلوك المختلف للكائن نفسه في مواقف مختلفة (بطريقة البرمجة ...يمكنك استدعاء وسائط مختلفة)

أعتقد أن المثال أدناه سيساعدك على فهم ...على الرغم من أنه ليس كود جافا خالصًا ...

     public void See(Friend)
     {
        System.out.println("Talk");
     }

ولكن إذا غيرنا الحجة ...سوف يتغير السلوك...

     public void See(Enemy)
     {
        System.out.println("Run");
     }

الشخص (هنا "الكائن") هو نفسه ...

تعدد الأشكال هو تطبيقات متعددة لكائن ما أو يمكنك قول أشكال متعددة لكائن ما.لنفترض أن لديك فصلًا دراسيًا Animals كفئة أساسية مجردة ولها طريقة تسمى movement() الذي يحدد الطريقة التي يتحرك بها الحيوان.الآن في الواقع لدينا أنواع مختلفة من الحيوانات وتتحرك بشكل مختلف أيضًا، بعضها بساقين، والبعض الآخر بأربعة، وبعضها بدون أرجل، وما إلى ذلك.لتحديد مختلفة movement() لكل حيوان على وجه الأرض، نحن بحاجة إلى تطبيق تعدد الأشكال.ومع ذلك، تحتاج إلى تحديد المزيد من الفئات، أي.فصل Dogs Cats Fish إلخ.فأنت بحاجة إلى توسيع تلك الفئات من الفئة الأساسية Animals وتجاوز طريقته movement() مع وظيفة حركة جديدة تعتمد على كل حيوان لديك.تستطيع ايضا استخذام Interfaces لتحقيق ذلك.الكلمة الأساسية هنا هي "تجاوز"، والتحميل الزائد مختلف ولا يعتبر تعددًا للأشكال.مع التحميل الزائد يمكنك تحديد طرق متعددة "بنفس الاسم" ولكن بمعلمات مختلفة على نفس الكائن أو الفئة.

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

على أية حال، الفرق بين المصطلحين يمكن شرحه بشكل أفضل باستخدام لغات أخرى، مثل c++:يتصرف الكائن متعدد الأشكال في لغة C++ كنظير Java إذا كانت الوظيفة الأساسية افتراضية، ولكن إذا لم تكن الطريقة افتراضية، فسيتم حل انتقال التعليمات البرمجية بشكل ثابت, ، ولم يتم التحقق من النوع الحقيقي في وقت التشغيل، لذلك، يتضمن تعدد الأشكال قدرة الكائن على التصرف بشكل مختلف اعتمادًا على الواجهة المستخدمة للوصول إليه؛اسمحوا لي أن أقدم مثالاً في الكود الكاذب:

class animal {
    public void makeRumor(){
        print("thump");
    }
}
class dog extends animal {
    public void makeRumor(){
        print("woff");
    }
}

animal a = new dog();
dog b = new dog();

a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

(بافتراض أن makeRumor ليس افتراضيًا)

لا تقدم Java حقًا هذا المستوى من تعدد الأشكال (يُسمى أيضًا تقطيع الكائنات).

حيوان أ = كلب جديد () ؛كلب ب = كلب جديد();

a.makeRumor() -> prints thump
b.makeRumor() -> prints woff

في كلتا الحالتين سيتم طباعة woff فقط ..نظرًا لأن a وb يشيران إلى كلب الصف

import java.io.IOException;

class Super {

    protected Super getClassName(Super s) throws IOException {
        System.out.println(this.getClass().getSimpleName() + " - I'm parent");
        return null;
    }

}

class SubOne extends Super {

    @Override
    protected Super getClassName(Super s)  {
        System.out.println(this.getClass().getSimpleName() + " - I'm Perfect Overriding");
        return null;
    }

}

class SubTwo extends Super {

    @Override
    protected Super getClassName(Super s) throws NullPointerException {
        System.out.println(this.getClass().getSimpleName() + " - I'm Overriding and Throwing Runtime Exception");
        return null;
    }

}

class SubThree extends Super {

    @Override
    protected SubThree getClassName(Super s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Returning SubClass Type");
        return null;
    }

}

class SubFour extends Super {

    @Override
    protected Super getClassName(Super s) throws IOException {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and Throwing Narrower Exception ");
        return null;
    }

}

class SubFive extends Super {

    @Override
    public Super getClassName(Super s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Overriding and have broader Access ");
        return null;
    }

}

class SubSix extends Super {

    public Super getClassName(Super s, String ol) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading ");
        return null;
    }

}

class SubSeven extends Super {

    public Super getClassName(SubSeven s) {
        System.out.println(this.getClass().getSimpleName()+ " - I'm Perfect Overloading because Method signature (Argument) changed.");
        return null;
    }

}

public class Test{

    public static void main(String[] args) throws Exception {

        System.out.println("Overriding\n");

        Super s1 = new SubOne(); s1.getClassName(null);

        Super s2 = new SubTwo(); s2.getClassName(null);

        Super s3 = new SubThree(); s3.getClassName(null);

        Super s4 = new SubFour(); s4.getClassName(null);

        Super s5 = new SubFive(); s5.getClassName(null);

        System.out.println("Overloading\n");

        SubSix s6 = new SubSix(); s6.getClassName(null, null);

        s6 = new SubSix(); s6.getClassName(null);

        SubSeven s7 = new SubSeven(); s7.getClassName(s7);

        s7 = new SubSeven(); s7.getClassName(new Super());

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