OOJS: البرمجة الكائنية Javaشرح كتابة البرامج النصية مع مثال

⚡ ملخص ذكي

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

  • 🧱 شاء: يقوم الكائن بتجميع الخصائص والأساليب ذات الصلة بحيث يمكن لمتغير واحد أن يمثل طالبًا أو حسابًا أو منزلًا.
  • ؟؟؟؟ ️ منشئو: دالة بناء مع يُنشئ العديد من النسخ من مخطط واحد قابل لإعادة الاستخدام.
  • 🔁 تعداد: تقوم حلقة for/in بالمرور على كل خاصية قابلة للتعداد، وطباعة المفاتيح والقيم دون ترميز الأسماء بشكل ثابت.
  • 📦 فئات ES6: الكلمة المفتاحية class هي اختصار برمجي؛ لا تزال الطرق موجودة على كائن النموذج الأولي المشترك.
  • 🧬 ميراث: يقوم بتوسيع نماذج الروابط تلقائيًا، ويستدعي super المُنشئ الأصل أو طريقة تم تجاوزها.
  • 🔒 التغليف: تبقى الحقول التي تبدأ بـ # خاصة، ومخفية عن Object.keys و JSON.stringify.
  • ⚙️ الممارسة: كل مثال في هذه الصفحة يعرض مخرجات تم التحقق منها، حتى تتمكن من مقارنة النتائج أثناء التعلم.

وجوه المنحى Javaمفاهيم البرمجة النصية (OOJS) مع الكائنات، والمنشئات، والفئات

ما هو مفهوم OOPS في Javaالبرنامج النصي؟

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

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

كيفية إنشاء كائن

يمكنك إنشاء كائن مثل هذا:

var objName = new Object();
objName.property1 = value1;
objName.property2 = value2;
objName.method1 = function()
{
  // line of code
}

أو باستخدام الصيغة الحرفية المختصرة للكائن:

var objName = { property1: value1, property2: value2, method1: function()
{
  // lines of code
} };

الوصول إلى خصائص الكائن وطرقه

يمكنك الوصول إلى خاصية من خصائص الكائن باستخدام تدوين النقطة، ويمكنك استدعاء طريقة بإضافة أقواس:

objectname.propertyname;   // read a property
objectname.methodname();   // call a method
objectname["propertyname"]; // bracket notation, useful for dynamic keys

جرب هذا المثال بنفسك:

<html>
<head>
	<title>Objects!!!</title>
	<script type="text/javascript">
      var student = new Object();
      student.fName = "John";
      student.lName = "Smith";
      student.id = 5;
      student.markE = 76;
      student.markM = 99;
      student.markS = 87;
      student.calculateAverage = function()
      {
      	return (student.markE + student.markM + student.markS)/3;
      };
      student.displayDetails = function()
      {
        document.write("Student Id: " + student.id + "<br />");
        document.write("Name: " + student.fName + " " + student.lName + "<br />");
        var avg = student.calculateAverage();
        document.write("Average Marks: " + avg);
      };
		student.displayDetails();
	</script>
</head>
<body>
</body>
</html>

الإخراج:

Student Id: 5
Name: John Smith
Average Marks: 87.33333333333333

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

عفوا منشئ

إنشاء الكائنات واحدًا تلو الآخر ليس مفيدًا جدًا، لأنه سيتعين عليك تكرار نفس الكود لكل طالب. وهنا يأتي دور مُنشئ الكائن. المُنشئ هو دالة عادية يتم استدعاؤها باستخدام جديد الكلمة المفتاحية؛ فهي تحدد نوع كائن يمكن إعادة استخدامه لكل مثيل فردي.

داخل الدالة البانية، يشير هذا إلى الكائن الجديد الذي يتم إنشاؤه، لذا تتلقى كل نسخة منه نسخة خاصة بها من القيم المُمرَّرة. غالبًا ما تُخزَّن القيم ذات الصلة، مثل العلامات، في مجموعة بحيث يمكن جمعها في عملية واحدة.

جرب هذا المثال بنفسك:

<html>
<head>
	<script type="text/javascript">
		function Student(first, last, id, english, maths, science)
        {
          this.fName = first;
          this.lName = last;
          this.id = id;
          this.markE = english;
          this.markM = maths;
          this.markS = science;
          this.calculateAverage = function()
          {
         	 return (this.markE + this.markM + this.markS)/3;
          }
		  this.displayDetails = function()
		  {
            document.write("Student Id: " + this.id + "<br />");
            document.write("Name: " + this.fName + " " + this.lName + "<br />");
            var avg = this.calculateAverage();
            document.write("Average Marks: " + avg + "<br /><br />");
		  }
		}
        var st1 = new Student("John", "Smith", 15, 85, 79, 90);
        var st2 = new Student("Hannah", "Turner", 23, 75, 80, 82);
        var st3 = new Student("Kevin", "White", 4, 93, 89, 90);
        var st4 = new Student("Rose", "Taylor", 11, 55, 63, 45);
        st1.displayDetails();
        st2.displayDetails();
        st3.displayDetails();
        st4.displayDetails();
	</script>
</head>
<body>
</body>
</html>

الإخراج:

Student Id: 15
Name: John Smith
Average Marks: 84.66666666666667

Student Id: 23
Name: Hannah Turner
Average Marks: 79

Student Id: 4
Name: Kevin White
Average Marks: 90.66666666666667

Student Id: 11
Name: Rose Taylor
Average Marks: 54.333333333333336

حلقة من خلال خصائص كائن

بناء الجملة:

for (variablename in objectname)
{
  // lines of code to be executed
}

لـ/في حلقة يُستخدم عادةً للتنقل بين خصائص الكائن. يمكنك إعطاء أي اسم له. متغيرلكن يجب أن يتطابق اسم الكائن مع كائن موجود بالفعل. في كل تمريرة، يحتفظ المتغير بالخاصية مفتاح كسلسلة نصية، لذا تُقرأ القيمة باستخدام صيغة الأقواس مثل: الموظف[س].

جرب هذا المثال بنفسك:

<html>
<head>
	<script type="text/javascript">
		var employee={first:"John", last:"Doe", department:"Accounts"};
        var details = "";
        document.write("<b>Using for/in loops </b><br />");
        for (var x in employee)
        {
          details = x + ": " + employee[x];
          document.write(details + "<br />");
        }
	</script>
</head>
<body>
</body>
</html>

الإخراج:

Using for/in loops
first: John
last: Doe
department: Accounts

هناك نقطتان جديرتان بالتذكر. تقوم حلقة for/in أيضًا بزيارة الخصائص القابلة للتعداد الموروثة عبر سلسلة النموذج الأولي، لذا فإن شرطًا مثل Object.prototype.hasOwnProperty.call(employee, x) غالباً ما تتم إضافتها. بالنسبة لكائنات البيانات البسيطة، Object.keys(employee) or Object.entries(employee) عادة ما يكون الأمر أكثر وضوحًا، لأن كليهما يعيد خصائص الكائن نفسه فقط.

ما هي صيغة فئة ES6؟ Javaالبرنامج النصي؟

أضافت ECMAScript 2015، والمعروفة باسم ES6، فئة الكلمة المفتاحية. الفئة هي اختصار برمجي لنظام النموذج الأولي الذي تستخدمه دالة البناء أعلاه بالفعل: لم يتم تقديم أي نموذج كائن جديد. تشغيل نوع الطالب لا تزال الدالة `on` في فئة ما تُرجع السلسلة النصية "function"، ويتم تثبيت كل طريقة مكتوبة في جسم الفئة على نموذج الطالبوهذا يعني أن جميع الحالات تشترك في نسخة واحدة من تلك الطريقة بدلاً من أن يحمل كل كائن نسخته الخاصة.

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

class Student {
  constructor(first, last, id, english, maths, science) {
    this.fName = first;
    this.lName = last;
    this.id = id;
    this.marks = [english, maths, science];
  }

  calculateAverage() {
    const total = this.marks.reduce((sum, m) => sum + m, 0);
    return total / this.marks.length;
  }

  displayDetails() {
    console.log("Student Id: " + this.id);
    console.log("Name: " + this.fName + " " + this.lName);
    console.log("Average Marks: " + this.calculateAverage().toFixed(2));
  }
}

const st1 = new Student("John", "Smith", 15, 85, 79, 90);
st1.displayDetails();
console.log(typeof Student);
console.log(st1.calculateAverage === Student.prototype.calculateAverage);

الإخراج:

Student Id: 15
Name: John Smith
Average Marks: 84.67
function
true

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

دالة البناء مقابل فئة ES6

البعد دالة البناء فئة ES6
إعلان دالة الطالب(…) { } class Student { constructor(…) { } }
طريقة تحديد موضع الجهاز يتم تعيينها داخل الدالة، لذا تحصل كل نسخة على نسختها الخاصة ما لم تكتب إلى نموذج الطالب مكتوب في جسم الفئة ويتم وضعه على النموذج الأولي تلقائيًا
الاتصال بدون جديد يعمل بصمت و غير مُعرَّف أو الكائن العام يُطلق خطأ من نوع TypeError على الفور
الرفع يتم رفع تعريفات الدوال واستخدامها قبل تعريفها. في منطقة زمنية ميتة حتى يتم تقييمها
الوضع الصارم يتبع النص المحيط دائما صارم
وراثة دليل: إنشاء الكائن بالإضافة إلى دعوة إلى الوالدين يمتد و السوبر
بيانات خاصة الإغلاقات أو اصطلاح التسمية مثل _توازن حقول خاصة حقيقية مع #
النموذج الأساسي نماذج النماذج الأولية (متطابقة أثناء التشغيل)

كيفية تطبيق الوراثة باستخدام extends و super

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

function Person(fName, lName) {
  this.fName = fName;
  this.lName = lName;
}

Person.prototype.introduce = function () {
  return "I am " + this.fName + " " + this.lName;
};

function Student(fName, lName, id) {
  Person.call(this, fName, lName);   // 1. borrow the parent constructor
  this.id = id;
}

Student.prototype = Object.create(Person.prototype); // 2. link the chain
Student.prototype.constructor = Student;              // 3. repair constructor

var st = new Student("Kevin", "White", 4);
console.log(st.introduce());
console.log(st instanceof Person);

الإخراج:

I am Kevin White
true

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

  • سوبر (…) كمكالمة يُشغّل هذا الأمر مُنشئ الأصل. وهو صالح فقط داخل مُنشئ المشتق، ويجب تشغيله قبل تعديل أي شيء آخر. وإلا سيتم طرح خطأ مرجعي.
  • super.method() لأن البحث عن الخاصية يستدعي النسخة الأصلية من طريقة قام الابن بتجاوزها، وهذه هي الطريقة التي تقوم بها بتوسيع السلوك بدلاً من استبداله.
class Person {
  constructor(fName, lName) {
    this.fName = fName;
    this.lName = lName;
  }

  introduce() {
    return "I am " + this.fName + " " + this.lName;
  }
}

class Student extends Person {
  constructor(fName, lName, id) {
    super(fName, lName);   // must run before "this" is used
    this.id = id;
  }

  introduce() {
    return super.introduce() + ", roll number " + this.id;
  }
}

const st = new Student("Hannah", "Turner", 23);
console.log(st.introduce());
console.log(st instanceof Student, st instanceof Person);
console.log(Object.getPrototypeOf(Student.prototype) === Person.prototype);

الإخراج:

I am Hannah Turner, roll number 23
true true
true

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

⚠️ تحذير: لا تُسند المهام أبدًا النموذج الأولي للطالب = النموذج الأولي للشخصهذا يجعل كلا النوعين يشتركان في كائن واحد، لذا فإن أي طريقة تضيفها إلى الكائن الفرعي تُضاف أيضًا إلى الكائن الأصل. استخدم Object.create(Person.prototype)أو ببساطة استخدم يمتد.

كيفية تحقيق التغليف باستخدام حقول الفئة الخاصة

التغليف يعني الحفاظ علىping الحالة الداخلية بعيدة المنال بحيث لا يمكن تغييرها إلا من خلال طرق تتحكم بها. لسنوات Javaقام مطورو البرامج النصية بتزييف الاسم باستخدام شرطة سفلية في البداية، لكن اسمًا مثل _توازن إنها مجرد اتفاقية، ويمكن لأي كود برمجي أن يتجاوزها. حديث Javaيوفر النص البرمجي خصوصية حقيقية: حقل يبدأ اسمه بـ # لا يمكن الوصول إليه إلا داخل جسم الفئة التي تُعلن عنه.

توفر الحقول الخاصة ثلاث فوائد ملموسة:

  • يجب تعريفها في جسم الفئة قبل الاستخدام، مما يوثق الحالة الداخلية للنوع في مكان واحد.
  • إنهم غير مرئيين لـ مفاتيح الكائن, JSON.stringifyو for/in، لذلك لا تتسرب الحالة الداخلية إلى المخرجات المتسلسلة.
  • إن الوصول إلى حقل خاص من خارج الفئة هو خطأ في بناء الجملة، يتم اكتشافه قبل تشغيل الكود، وليس خطأً صامتًا.
class BankAccount {
  #balance = 0;

  constructor(owner, opening) {
    this.owner = owner;
    this.#balance = opening;
  }

  deposit(amount) {
    if (amount <= 0) {
      return "Deposit must be positive";
    }
    this.#balance += amount;
    return this.owner + " balance: " + this.#balance;
  }

  get balance() {
    return this.#balance;
  }
}

const acc = new BankAccount("John", 500);
console.log(acc.deposit(250));
console.log(acc.deposit(-40));
console.log("Read via getter: " + acc.balance);
console.log("Own keys: " + Object.keys(acc));
console.log("JSON: " + JSON.stringify(acc));

الإخراج:

John balance: 750
Deposit must be positive
Read via getter: 750
Own keys: owner
JSON: {"owner":"John"}

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

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

الأسئلة الشائعة

يقوم هذا الإجراء بإنشاء كائن فارغ، وربطه بنموذج المُنشئ، ثم يُشغّل المُنشئ مع ربط "this" بهذا الكائن، ثم يُعيده. إذا أعاد المُنشئ كائنًا بشكل صريح، فسيتم إرجاع تلك القيمة بدلاً من ذلك.

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

لأنّ "this" يُحدّد بناءً على كيفية استدعاء الدالة، وليس مكان كتابتها. تمرير دالة كدالة رد نداء يُفقد المُستقبِل. يُمكن حلّ هذه المشكلة بربط الدالة في المُنشئ أو بتعريفها كحقل فئة باستخدام دالة سهمية.

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

نعم. مساعدو البرمجة المدعومون بالذكاء الاصطناعي يتعاملون مع عملية إعادة الهيكلة هذه بشكل جيد لأن الخريطةping إنها آلية. راجع النتيجة دائمًا، لأن المساعدين قد يغفلون عن الطرق المرفقة بالنموذج الأولي خارج المُنشئ أو يتجاهلون استدعاء المُنشئ الأصل.

نعم. لا يزال يتعين قراءة الكود المُولّد ومراجعته وتصحيح أخطائه. إن معرفة كيفية عمل النماذج الأولية، و"this"، والحقول الخاصة هي ما يُمكّنك من اكتشاف أي اقتراح خاطئ قبل وصوله إلى مرحلة الإنتاج.

تلخيص هذه التدوينة بـ: