جاوا یک زبان شی گرا است. این بدان معنی است که شما باید برنامه های جاوا را با استفاده از یک سبک شی گرا بنویسید. و این سبک بر اساس استفاده از آبجکت ها و کلاس ها در برنامه است.

اصول اولیه OOP:

اصول OOP - 1بیایید سعی کنیم با کمک مثال ها بفهمیم که کلاس ها و اشیاء چیست و همچنین چگونه اصول اولیه OOP را در عمل اعمال کنیم: انتزاع، وراثت، چندشکلی و کپسوله سازی.

شی چیست؟

جهانی که ما در آن زندگی می کنیم از اشیا تشکیل شده است. اگر به اطراف نگاه کنیم، می بینیم که اطرافمان را خانه ها، درختان، ماشین ها، مبلمان، ظرف ها، کامپیوترها احاطه کرده اند. همه این موارد، اشیا هستند و هر یک از آنها دارای مجموعه ای از ویژگی ها، رفتار و هدف خاصی هستند. ما به اشیا عادت داریم و همیشه از آنها برای اهداف بسیار خاص استفاده می کنیم. مثلاً اگر نیاز به سر کار داریم از ماشین استفاده می کنیم، اگر می خواهیم غذا بخوریم از ظروف و اگر نیاز به استراحت داریم به یک مبل راحتی نیاز داریم. انسان عادت دارد برای حل مشکلات زندگی روزمره به طور عینی فکر کند. این یکی از دلایل استفاده از آبجکت ها در برنامه نویسی بود و این رویکرد برای ایجاد برنامه ها را شی گرا می نامیدند. بیایید یک مثال بزنیم. تصور کنید که یک مدل گوشی جدید تولید کرده اید و می خواهید تولید انبوه آن را راه اندازی کنید. به عنوان یک طراح تلفن، شما می دانید که برای چه چیزی است، چگونه کار می کند و از چه قطعاتی تشکیل شده است (قاب، میکروفون، بلندگو، سیم ها، دکمه ها و غیره). با این حال، فقط شما می دانید که چگونه این قطعات را به هم وصل کنید. با این حال، شما برنامه ای برای تولید تلفن های شخصی ندارید؛ برای این کار شما یک پرسنل کامل دارید. برای اینکه مجبور نباشید هر بار نحوه اتصال قطعات گوشی را توضیح دهید و همه گوشی های در حال تولید یکسان شوند، قبل از شروع به تولید آنها باید یک نقاشی به شکل یک طراحی کنید. توضیح ساختار گوشی در OOP، چنین توصیف، ترسیم، نمودار یا قالب را یک کلاس می گویند که در هنگام اجرای برنامه یک شی از آن ایجاد می شود. یک کلاس توصیفی از یک شی است که هنوز ایجاد نشده است، مانند یک الگوی کلی که از فیلدها، متدها و یک سازنده تشکیل شده است، و یک شی نمونه ای از یک کلاس است که بر اساس این توضیحات ایجاد شده است.

انتزاع OOP

بیایید اکنون به این فکر کنیم که چگونه می توانیم از یک شی در دنیای واقعی به یک شی در یک برنامه، با استفاده از تلفن به عنوان مثال، حرکت کنیم. تاریخچه این وسیله ارتباطی بیش از 100 سال است و تلفن مدرن، برخلاف مدل قبلی خود در قرن 19، دستگاه بسیار پیچیده تری است. وقتی از یک تلفن استفاده می کنیم، به ساختار آن و فرآیندهایی که در داخل آن رخ می دهد فکر نمی کنیم. ما به سادگی از عملکردهای ارائه شده توسط توسعه دهندگان تلفن استفاده می کنیم - دکمه ها یا صفحه لمسی برای انتخاب شماره و برقراری تماس. یکی از اولین رابط های تلفن، دستگیره ای بود که برای برقراری تماس می چرخید. البته این خیلی راحت نبود. با این وجود، دسته عملکرد خود را به درستی انجام داد. اگر به مدرن ترین و اولین تلفن نگاه کنید، می توانید بلافاصله مهم ترین جزئیاتی را که هم برای دستگاهی از اواخر قرن نوزدهم و هم برای یک تلفن هوشمند فوق مدرن مهم هستند، شناسایی کنید. این یعنی برقراری تماس (شماره گیری) و دریافت تماس. در اصل این چیزی است که یک گوشی را به یک گوشی تبدیل می کند و نه چیز دیگری. اکنون ما اصل را در OOP اعمال کرده ایم - برجسته کردن مهمترین ویژگی ها و اطلاعات در مورد یک شی. این اصل OOP انتزاع نامیده می شود. انتزاع در OOP همچنین می تواند به عنوان راهی برای نمایش عناصر یک مسئله دنیای واقعی به عنوان اشیا در یک برنامه تعریف شود. انتزاع همیشه با تعمیم برخی از اطلاعات در مورد ویژگی های اشیا یا اشیا همراه است، بنابراین نکته اصلی جدا کردن اطلاعات مهم از اطلاعات ناچیز در زمینه مشکل در حال حل است. در این حالت، چندین سطح از انتزاع می تواند وجود داشته باشد. بیایید سعی کنیم اصل انتزاع را در گوشی های خود اعمال کنیم. ابتدا، بیایید متداول ترین انواع گوشی ها را از ابتدا تا امروز برجسته کنیم. به عنوان مثال، آنها را می توان در قالب یک نمودار نشان داده شده در شکل 1 نشان داد. اصول OOP - 2اکنون، با کمک انتزاع، می توانیم اطلاعات کلی را در این سلسله مراتب اشیاء برجسته کنیم: یک نوع انتزاعی رایج از اشیاء - تلفن، یک ویژگی کلی از تلفن - سال ایجاد آن و یک رابط مشترک - همه تلفن ها قادر به دریافت و ارسال تماس هستند. در جاوا به این صورت است:
public abstract class AbstractPhone {
    private int year;

    public AbstractPhone(int year) {
        this.year = year;
    }
    public abstract void call(int outputNumber);
    public abstract void ring (int inputNumber);
}
بر اساس این کلاس انتزاعی، ما قادر خواهیم بود انواع جدیدی از گوشی ها را در برنامه با استفاده از سایر اصول اولیه Java OOP ایجاد کنیم که در ادامه به بررسی آنها می پردازیم.

کپسوله سازی

با کمک انتزاع، آنچه در همه اشیاء مشترک است را برجسته می کنیم. با این حال، هر مدل گوشی فردی است و تا حدودی با مدل های دیگر متفاوت است. چگونه می توانیم در برنامه مرزبندی کنیم و این فردیت را مشخص کنیم؟ چگونه می توانیم مطمئن شویم که هیچ یک از کاربران نمی توانند به طور تصادفی یا عمدی گوشی ما را بشکنند یا سعی کنند یک مدل را به مدل دیگر تبدیل کنند؟ برای دنیای اشیاء واقعی، پاسخ واضح است: شما باید تمام قطعات را در بدنه گوشی قرار دهید. از این گذشته، اگر ما این کار را انجام ندهیم و تمام قسمت های داخلی تلفن و سیم های متصل کننده آنها را بیرون بگذاریم، قطعا یک آزمایشگر کنجکاو وجود خواهد داشت که می خواهد عملکرد تلفن ما را "بهبود" دهد. برای جلوگیری از چنین تداخلی در طراحی و عملکرد یک شی، OOP از اصل کپسوله‌سازی استفاده می‌کند - یکی دیگر از اصول اولیه OOP، که در آن ویژگی‌ها و رفتار یک شی در یک کلاس ترکیب می‌شوند، اجرای داخلی شی از آن پنهان می‌شود. کاربر، و یک رابط باز برای کار با شی ارائه شده است. وظیفه برنامه نویس این است که تعیین کند کدام ویژگی ها و روش ها برای عموم قابل دسترسی هستند و کدام یک پیاده سازی داخلی شی هستند و نباید اصلاح شوند.

کپسوله سازی و کنترل دسترسی

بیایید بگوییم که در طول تولید، اطلاعات مربوط به آن در پشت تلفن حک شده است: سال ساخت آن یا آرم شرکت سازنده. این اطلاعات کاملاً مشخص کننده این مدل - شرایط آن است. می توان گفت که توسعه دهنده تلفن از تغییرناپذیری این اطلاعات مراقبت کرده است - بعید است که کسی به فکر حذف حکاکی باشد. در دنیای جاوا، وضعیت اشیاء آینده در یک کلاس با استفاده از فیلدها توصیف می شود و رفتار آنها با استفاده از روش ها توصیف می شود. توانایی تغییر حالت و رفتار با استفاده از اصلاح کننده های دسترسی به فیلدها و روش ها - private, protected, public, و default(دسترسی پیش فرض) انجام می شود. به عنوان مثال، ما تصمیم گرفتیم که سال ساخت، نام سازنده گوشی و یکی از متدها متعلق به پیاده سازی داخلی کلاس باشد و توسط سایر آبجکت های برنامه قابل تغییر نباشد. با استفاده از کد، کلاس را می توان به صورت زیر توصیف کرد:
public class SomePhone {

    private int year;
    private String company;
    public SomePhone(int year, String company) {
        this.year = year;
        this.company = company;
    }
private void openConnection(){
    //findComutator
    //openNewConnection...
}
public void call() {
    openConnection();
    System.out.println("I'm calling a number");
}

public void ring() {
    System.out.println("Дзынь-дзынь");
}

 }
یک اصلاح کننده privateفیلدها و متدهای یک کلاس را فقط در آن کلاس در دسترس قرار می دهد. این بدان معنی است که privateنمی توان به فیلدها از خارج دسترسی داشت و همچنین نمی توان privateمتدها را فراخوانی کرد. پنهان کردن دسترسی به یک متد openConnectionهمچنین این فرصت را به ما می‌دهد تا آزادانه اجرای داخلی این روش را تغییر دهیم، زیرا این روش تضمین شده است که توسط اشیاء دیگر استفاده نمی‌شود و عملکرد آنها را مختل نمی‌کند. callبرای کار با شی ما، روش ها را با استفاده از ringاصلاح کننده باز می گذاریم public. ارائه روش های عمومی برای کار با یک شی نیز بخشی از مکانیسم کپسوله سازی است، زیرا اگر دسترسی به یک شی به طور کامل ممنوع شود، بی فایده خواهد شد.

وراثت

Давайте посмотрим еще раз на диаграмму телефонов. Можно заметить, что она представляет собой иерархию, в которой модель, расположенная ниже обладает всеми признаками моделей, расположенных выше по ветке, плюс своими собственными. Например, смартфон, использует сотовую сеть для связи (обладает свойствами сотового телефона), является беспроводным и переносным (обладает свойствами беспроводного телефона) и может принимать и делать вызовы (свойствами телефона). В этом случае мы можем говорить о наследовании свойств an object. В программировании наследование заключается в использовании уже существующих классов для описания новых. Рассмотрим пример создания класса смартфон с помощью наследования. Все беспроводные телефоны работают от аккумуляторных батарей, которые имеют определенный ресурс работы в часах. Поэтому добавим это свойство в класс беспроводных телефонов:
public abstract class WirelessPhone extends AbstractPhone {

    private int hour;

    public WirelessPhone(int year, int hour) {
        super(year);
        this.hour = hour;
    }
    }
Сотовые телефоны наследуют свойства беспроводного телефона, мы также добавor в этот класс реализацию методов call и ring:
public class CellPhone extends WirelessPhone {
    public CellPhone(int year, int hour) {
        super(year, hour);
    }

    @Override
    public void call(int outputNumber) {
        System.out.println("Calling a number" + outputNumber);
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("A subscriber is calling you" + inputNumber);
    }
}
И, наконец, класс смартфон, который в отличие от классических сотовых телефонов имеет полноценную операционную систему. В смартфон можно добавлять новые программы, поддерживаемые данной операционной системой, расширяя, таким образом, его функциональность. С помощью codeа класс можно описать так:
public class Smartphone extends CellPhone {

    private String operationSystem;

    public Smartphone(int year, int hour, String operationSystem) {
        super(year, hour);
        this.operationSystem = operationSystem;
    }
public void install(String program){
    System.out.println("Installing" + program + "For" + operationSystem);
}

}
Как видите, для описания класса Smartphone мы создали совсем немного нового codeа, но получor новый класс с новой функциональностью. Использование принципа наследование ООП позволяет значительно уменьшить объем codeа, а значит, и облегчить работу программисту.

Полиморфизм

Если мы посмотрим на все модели телефонов, то, несмотря на различия во внешнем облике и устройстве моделей, мы можем выделить у них некое общее поведение – все они могут принимать и совершать звонки и имеют достаточно понятный и простой набор кнопок управления. Применяя известный нам уже один из основных принципов ООП абстракцию в терминах программирования можно сказать, что an object телефон имеет один общий интерфейс. Поэтому пользователи телефонов могут вполне комфортно пользоваться различными моделями, используя одни и те же кнопки управления (механические or сенсорные), не вдаваясь в технические тонкости устройства. Так, вы постоянно пользуетесь сотовым телефоном, и без труда сможете совершить звонок с его стационарного собрата. Принцип в ООП, когда программа может использовать an objectы с одинаковым интерфейсом без информации о внутреннем устройстве an object, называется полиморфизмом. Давайте представим, что нам в программе нужно описать пользователя, который может пользоваться любыми моделями телефона, чтобы позвонить другому пользователю. Вот How можно это сделать:
public class User {
    private String name;

    public User(String name) {
        this.name = name;
            }

    public void callAnotherUser(int number, AbstractPhone phone){
// here it is polymorphism - using the abstract type AbstractPhone phone in the code!
        phone.call(number);
    }
}
 }
Теперь опишем различные модели телефонов. Одна из первых моделей телефонов:
public class ThomasEdisonPhone extends AbstractPhone {

public ThomasEdisonPhone(int year) {
    super(year);
}
    @Override
    public void call(int outputNumber) {
        System.out.println("Turn the Handle");
        System.out.println("Give me the phone number, sir");
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
Обычный стационарный телефон:
public class Phone extends AbstractPhone {

    public Phone(int year) {
        super(year);
    }

    @Override
    public void call(int outputNumber) {
        System.out.println("I'm calling a number" + outputNumber);
    }

    @Override
    public void ring(int inputNumber) {
        System.out.println("Phone calls");
    }
}
И, наконец, крутой видеотелефон:
public class VideoPhone extends AbstractPhone {

    public VideoPhone(int year) {
        super(year);
    }
    @Override
    public void call(int outputNumber) {
        System.out.println("I connect a video channel for the subscriber" + outputNumber );
    }
    @Override
    public void ring(int inputNumber) {
        System.out.println("You have an incoming video call..." + inputNumber);
    }
  }
Создадим an objectы в методе main() и протестируем метод callAnotherUser:
AbstractPhone firstPhone = new ThomasEdisonPhone(1879);
AbstractPhone phone = new Phone(1984);
AbstractPhone videoPhone=new VideoPhone(2018);
User user = new User("Andrey");
user.callAnotherUser(224466,firstPhone);
// Rotate the knob
// Tell me the number of the subscriber, sir
user.callAnotherUser(224466,phone);
//Call number 224466
user.callAnotherUser(224466,videoPhone);
//I connect the video channel for subscriber 224466
با فراخوانی یک متد روی شی user، نتایج متفاوتی گرفتیم. انتخاب یک پیاده سازی متد خاص callدر یک متد callAnotherUserبه صورت پویا بر اساس نوع خاص شیء فراخوان در طول اجرای برنامه انجام شد. این مزیت اصلی پلی مورفیسم است - انتخاب پیاده سازی در طول اجرای برنامه. در مثال‌های کلاس تلفن بالا، از متد overriding استفاده کردیم، تکنیکی که پیاده‌سازی متد تعریف‌شده در کلاس پایه را بدون تغییر امضای متد تغییر می‌دهد. این اساساً یک جایگزین متد است و متد جدیدی است که در زیر کلاس تعریف شده است که هنگام اجرای برنامه فراخوانی می شود. به طور معمول، هنگام نادیده گرفتن یک متد، از حاشیه نویسی استفاده می شود @Overrideکه به کامپایلر می گوید امضای متدهای لغو شده و نادیده گرفته شده را بررسی کند. در نتیجه ، برای اطمینان از اینکه سبک برنامه شما با مفهوم OOP و اصول OOP java مطابقت دارد، نکات زیر را دنبال کنید:
  • برجسته کردن ویژگی های اصلی شی.
  • مشخص کردن خصوصیات و رفتار مشترک و استفاده از وراثت هنگام ایجاد اشیا.
  • از انواع انتزاعی برای توصیف اشیا استفاده کنید.
  • سعی کنید همیشه متدها و فیلدهای مربوط به پیاده سازی داخلی کلاس را مخفی کنید.