CodeGym /وبلاگ جاوا /Random-FA /سازندگان جاوا
John Squirrels
مرحله
San Francisco

سازندگان جاوا

در گروه منتشر شد
سلام! امروز یک موضوع بسیار مهم را در نظر خواهیم گرفت که به اشیاء ما مربوط می شود. بدون اغراق، می توان گفت که شما هر روز از این موضوع در زندگی واقعی استفاده خواهید کرد! ما در مورد سازندگان جاوا صحبت می کنیم. شاید این اولین باری باشد که این اصطلاح را می شنوید، اما در واقع قبلاً از سازنده ها استفاده کرده اید. فقط متوجه نشدی :) بعدا خودمون رو قانع می کنیم.

سازندگان در دنیا چیست و چرا به آنها نیاز است؟

بیایید دو مثال را در نظر بگیریم.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 378;

   }
}
ما ماشین خود را ساختیم و مدل و حداکثر سرعت آن را تنظیم کردیم. اما شی Car بدیهی است که در یک پروژه واقعی 2 فیلد نخواهد داشت. مثلا ممکنه 16 فیلد داشته باشه!
public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

   }

}
ما یک شی جدید Car ایجاد کرده ایم . یک مشکل وجود دارد: ما 16 فیلد داریم، اما فقط 12 را مقداردهی اولیه کردیم ! اکنون به کد نگاه کنید و سعی کنید فیلدهایی را که فراموش کرده ایم پیدا کنید! به این راحتی نیست، نه؟ در این شرایط، یک برنامه نویس به راحتی می تواند اشتباه کند و در مقداردهی اولیه فیلد شکست بخورد. در نتیجه، برنامه نادرست رفتار خواهد کرد:
public class Car {

   String model;// model
   int maxSpeed;// maximum speed
   int wheels;// wheel width
   double engineVolume;// engine volume
   String color;// color
   int productionYear;// production year
   String ownerFirstName;// first name of owner
   String ownerLastName;// last name of owner
   long price;// price
   boolean isNew;// flag indicating whether car is new
   int seatsInTheCar;// number of seats in the car
   String cabinMaterial;// interior material
   boolean insurance;// flag indicating whether car is insured
   String manufacturerCountry;// manufacturer country
   int trunkVolume;// size of the trunk
   int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)


   public static void main(String[] args) {
       Car bugatti = new Car();

       bugatti.color = "blue";
       bugatti.accelerationTo100km = 3;
       bugatti.engineVolume = 6.3;
       bugatti.manufacturerCountry = "Italy";
       bugatti.ownerFirstName = "Amigo";
       bugatti.productionYear = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.seatsInTheCar = 2;
       bugatti.maxSpeed = 378;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model: Bugatti Veyron. Engine volume: " + bugatti.engineVolume + ". Trunk volume: " + bugatti.trunkVolume + ". Cabin material: " + bugatti.cabinMaterial +
       ". Wheel width: " + bugatti.wheels + ". Purchased in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
خروجی کنسول: مدل: بوگاتی ویرون. حجم موتور: 6.3. حجم صندوق عقب: 0. جنس کابین: پوچ. عرض چرخ: 0. خرید در سال 2018 توسط آقای null خریدار شما که 2 میلیون دلار برای ماشین صرف نظر کرده است، بدیهی است که دوست ندارد او را " آقای نول " خطاب کنند! اما به طور جدی، نکته اصلی این است که برنامه ما یک شی را به اشتباه ایجاد کرده است: یک ماشین با عرض چرخ 0 (یعنی اصلا چرخی ندارد)، یک صندوق عقب گم شده، یک کابین ساخته شده از مواد ناشناخته، و مهمتر از همه، یک مالک نامشخص . شما فقط می توانید تصور کنید که چگونه چنین اشتباهی می تواند هنگام اجرای برنامه "خاموش شود"! ما باید به نحوی از چنین موقعیت هایی اجتناب کنیم. ما باید برنامه خود را محدود کنیم: هنگام ایجاد یک آبجکت جدید Car ، می خواهیم فیلدهایی مانند مدل و حداکثر سرعت همیشه مشخص شوند. در غیر این صورت می خواهیم از ایجاد شیء جلوگیری کنیم. سازندگان این کار را به راحتی انجام می دهند. آنها نام خود را به دلیلی دریافت کردند. سازنده نوعی کلاس "اسکلت" ایجاد می کند که هر شی جدید باید مطابقت داشته باشد. برای راحتی کار، اجازه دهید به نسخه ساده تر کلاس خودرو با دو فیلد بازگردیم . با توجه به نیازهای ما، سازنده کلاس خودرو به شکل زیر خواهد بود:
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}

// And creating an object now looks like this:

public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 378);
}
توجه داشته باشید که چگونه یک سازنده اعلام می شود. این شبیه به یک روش معمولی است، اما نوع بازگشتی ندارد. علاوه بر این، سازنده نام کلاس ( Car ) را مشخص می کند که با یک حرف بزرگ شروع می شود. علاوه بر این، سازنده با یک کلمه کلیدی استفاده می شود که برای شما جدید است: این . کلمه کلیدی this برای نشان دادن یک شی خاص است. کد موجود در سازنده
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
می‌توان تقریباً کلمه به کلمه تفسیر کرد: " مدل این ماشین (مدلی که اکنون می‌سازیم) آرگومان مدل است که به سازنده ارسال شده است . سازنده." و این اتفاقی است که می افتد:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car("Bugatti Veyron", 378);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
خروجی کنسول: Bugatti Veyron 378 سازنده به درستی مقادیر مورد نیاز را اختصاص داده است. شاید متوجه شده باشید که یک سازنده بسیار شبیه به یک روش معمولی است! پس اینطور. سازنده واقعاً یک متد است، اما با ویژگی های خاص :) درست مانند متدها، ما آرگومان هایی را به سازنده خود منتقل کردیم. و درست مانند فراخوانی یک متد، فراخوانی سازنده کار نخواهد کرد مگر اینکه آنها را مشخص کنید:
public class Car {

   String model;
   int maxSpeed;

   public Car(String model, int maxSpeed) {
       this.model = model;
       this.maxSpeed = maxSpeed;
   }

   public static void main(String[] args) {
       Car bugatti = new Car(); // Error!
   }

}
می توانید ببینید که سازنده آنچه را که ما در تلاش برای رسیدن به آن بودیم به انجام می رساند. حالا شما نمی توانید یک ماشین بدون سرعت یا مدل بسازید! شباهت بین سازنده ها و متدها به اینجا ختم نمی شود. درست مانند متدها، سازنده ها می توانند بیش از حد بارگذاری شوند. تصور کنید 2 گربه خانگی در خانه دارید. شما یکی از آنها را به عنوان یک بچه گربه گرفتید. اما دومی را زمانی که بزرگ شده بود از خیابان گرفتید و دقیقاً نمی دانید چند ساله است. در این مورد، ما می خواهیم که برنامه ما بتواند دو نوع گربه ایجاد کند: گربه هایی با نام و سن (برای گربه اول)، و گربه هایی که فقط نام دارند (برای گربه دوم). برای این کار سازنده را اضافه بار می کنیم:
public class Cat {

   String name;
   int age;

   // For the first cat
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For the second cat
   public Cat(String name) {
       this.name = name;
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
علاوه بر سازنده اصلی با پارامترهای "name" و "age"، ما یک مورد دیگر را تنها با یک پارامتر نام اضافه کردیم. دقیقاً به همان روشی که در درس های قبلی روش ها را اضافه بارگذاری کردیم. اکنون می توانیم هر دو نوع گربه ایجاد کنیم :)
چرا به سازندگان نیاز داریم؟  - 2
به یاد داشته باشید که در ابتدای درس گفتیم که قبلاً بدون اینکه متوجه شوید از سازنده ها استفاده کرده اید؟ منظورمان همان چیزی بود که گفتیم. واقعیت این است که هر کلاس در جاوا دارای چیزی است که سازنده پیش فرض نامیده می شود. هیچ آرگومان نمی گیرد، اما هر بار که هر شیء از هر کلاسی را ایجاد می کنید، فراخوانی می شود.
public class Cat {

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
در نگاه اول، نامرئی است. ما یک شی ایجاد کردیم، پس چه؟ سازنده کجا در اینجا کاری انجام می دهد؟ برای دیدن آن، بیایید به صراحت یک سازنده خالی برای کلاس Cat بنویسیم . چند عبارت را در داخل آن نمایش خواهیم داد. اگر عبارت نمایش داده شود، سازنده فراخوانی شده است.
public class Cat {

   public Cat() {
       System.out.println("A cat has been created!");
   }

   public static void main(String[] args) {

       Cat smudge = new Cat(); // The default constructor is invoked here
   }
}
خروجی کنسول: یک گربه ایجاد شد! تایید وجود دارد! سازنده پیش فرض همیشه به صورت نامرئی در کلاس های شما وجود دارد. اما باید یک چیز دیگر در مورد آن بدانید. هنگامی که سازنده ای با آرگومان ایجاد کنید، سازنده پیش فرض از یک کلاس حذف می شود. در واقع، ما قبلاً شاهد این موضوع در بالا بودیم. در این کد بود:
public class Cat {

   String name;
   int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat smudge = new Cat(); //Error!
   }
}
ما نمی‌توانستیم یک Cat بدون نام و سن ایجاد کنیم، زیرا سازنده Cat را با پارامترهای رشته و int اعلام کردیم. این باعث شد که سازنده پیش فرض بلافاصله از کلاس ناپدید شود. بنابراین حتما به خاطر داشته باشید که اگر به چندین سازنده در کلاس خود نیاز دارید، از جمله سازنده بدون آرگومان، باید آن را جداگانه اعلام کنید. به عنوان مثال، فرض کنید در حال ایجاد یک برنامه برای یک کلینیک دامپزشکی هستیم. کلینیک ما می خواهد کارهای خیر انجام دهد و به بچه گربه های بی خانمانی که نام و سن آنها مشخص نیست کمک کند. سپس کد ما باید به شکل زیر باشد:
public class Cat {

   String name;
   int age;

   // For cats with owners
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   // For street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 5);
       Cat streetCat = new Cat();
   }
}
اکنون که یک سازنده پیش‌فرض واضح نوشته‌ایم، می‌توانیم هر دو نوع cat را ایجاد کنیم :) مانند هر روش دیگری، ترتیب آرگومان‌های ارسال شده به سازنده بسیار مهم است. بیایید آرگومان های نام و سن را در سازنده خود عوض کنیم.
public class Cat {

   String name;
   int age;

   public Cat(int age, String name) {
       this.name = name;
       this.age = age;
   }

   public static void main(String[] args) {

       Cat smudge = new Cat("Smudge", 10); // Error!
   }
}
یک خطا! سازنده به وضوح تصریح می کند که وقتی یک شی Cat ایجاد می شود، باید یک عدد و یک رشته به این ترتیب ارسال شود. بنابراین، کد ما کار نمی کند. حتماً این را به خاطر داشته باشید و هنگام اعلام کلاس های خود آن را در نظر داشته باشید:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}

public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
این دو سازنده کاملاً متفاوت هستند! اگر بخواهیم پاسخ سوال "چرا به سازنده نیاز دارم؟" را در یک جمله بیان کنیم، ممکن است بگوییم "برای اطمینان از اینکه اشیا همیشه حالت معتبری دارند". وقتی از سازنده ها استفاده می کنید، همه متغیرهای شما به درستی مقداردهی اولیه می شوند. برنامه های شما هیچ ماشینی با سرعت 0 یا هر شی "نامعتبر" دیگری نخواهد داشت. مزیت اصلی آنها برای برنامه نویس است. اگر فیلدها را به صورت دستی (بعد از ایجاد یک شی) مقداردهی اولیه کنید، این خطر بزرگ وجود دارد که چیزی را از دست بدهید و یک اشکال ایجاد کنید. اما این اتفاق با سازنده نمی‌افتد: اگر نتوانید همه آرگومان‌های مورد نیاز را پاس کنید یا انواع آرگومان‌های اشتباه را ارسال کنید، کامپایلر بلافاصله خطا را ثبت می‌کند. همچنین باید به طور جداگانه بگوییم که منطق برنامه خود را در داخل سازنده قرار ندهید. روش ها برای این کار است. روش ها جایی هستند که شما باید تمام عملکردهای مورد نیاز را تعریف کنید. بیایید ببینیم چرا اضافه کردن منطق به سازنده ایده بدی است:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factory is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
ما یک کلاس CarFactory داریم که کارخانه ماشین را توصیف می کند. در داخل سازنده، همه فیلدها را مقداردهی اولیه می کنیم و منطقی را در آن قرار می دهیم: اطلاعاتی در مورد کارخانه نمایش می دهیم. به نظر می رسد هیچ چیز بدی در این مورد وجود ندارد. برنامه به خوبی کار می کند. خروجی کنسول: کارخانه خودروسازی ما فورد نام دارد که 115 سال پیش تاسیس شد از آن زمان تاکنون 50000000 خودرو تولید کرده است به طور متوسط ​​سالانه 434782 خودرو تولید می کند اما ما در واقع یک معدن با تاخیر زمانی ایجاد کرده ایم. و این نوع کد به راحتی می تواند منجر به خطا شود. فرض کنید اکنون صحبت از فورد نیست، بلکه در مورد کارخانه جدیدی به نام "آمیگو موتورز" است که کمتر از یک سال است که وجود دارد و 1000 خودرو تولید کرده است:
public class CarFactory {

   String name;
   int age;
   int carsCount;

   public CarFactory(String name, int age, int carsCount) {
   this.name = name;
   this.age = age;
   this.carsCount = carsCount;

   System.out.println("Our car factor is called " + this.name);
   System.out.println("It was founded " + this.age + " years ago" );
   System.out.println("Since that time, it has produced " + this.carsCount +  " cars");
   System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
خروجی کنسول: کارخانه خودروسازی ما Amigo Motors Exception در موضوع "اصلی" java.lang نامیده می شود. ArithmeticException: / توسط صفر 0 سال پیش تاسیس شد از آن زمان تاکنون 1000 خودرو در CarFactory تولید شده است. (CarFactory.java:15) در CarFactory.main (CarFactory.java:23) فرآیند با کد خروج 1 به پایان رسید رونق! برنامه با نوعی خطای نامفهوم به پایان می رسد. آیا می توانید سعی کنید علت را حدس بزنید؟ مشکل در منطقی است که در سازنده قرار داده ایم. به طور دقیق تر، این خط:
System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
در اینجا شما در حال انجام یک محاسبه و تقسیم تعداد خودروهای تولید شده بر سن کارخانه هستید. و از آنجایی که کارخانه ما جدید است (یعنی 0 ساله است) بر 0 تقسیم می کنیم که در ریاضی نمی توانیم انجام دهیم. در نتیجه، برنامه با یک خطا خاتمه می یابد.

باید چیکار میکردیم؟

تمام منطق را در یک روش جداگانه قرار دهید. بیایید آن را printFactoryInfo() بنامیم . می توانید یک شی CarFactory را به عنوان آرگومان به آن ارسال کنید. شما می توانید تمام منطق را در آنجا قرار دهید، و به طور همزمان با خطاهای احتمالی (مانند خطاهای ما که شامل صفر سال است) رسیدگی کنید. به هر کدام مال خودش برای تنظیم وضعیت آبجکت معتبر به سازنده ها نیاز است. ما روش هایی برای منطق تجاری داریم. یکی را با دیگری مخلوط نکنید برای تقویت آموخته هایتان، پیشنهاد می کنیم یک درس ویدیویی از دوره جاوا ما تماشا کنید
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION