JavaRush /จาวาบล็อก /Random-TH /คอฟฟี่เบรค#90. 4 เสาหลักของการเขียนโปรแกรมเชิงวัตถุ

คอฟฟี่เบรค#90. 4 เสาหลักของการเขียนโปรแกรมเชิงวัตถุ

เผยแพร่ในกลุ่ม
ที่มา: The Geek Asian เรามาดูปัจจัยพื้นฐานสี่ประการของการเขียนโปรแกรมเชิงวัตถุแล้วพยายามทำความเข้าใจวิธีการทำงาน การเขียนโปรแกรมเชิงวัตถุ (OOP) เป็นหนึ่งในกระบวนทัศน์การเขียนโปรแกรมหลัก อาจเป็นเรื่องง่ายและเรียบง่ายหรือในทางกลับกันซับซ้อนมาก ทุกอย่างขึ้นอยู่กับว่าคุณตัดสินใจพัฒนาแอปพลิเคชันของคุณอย่างไร คอฟฟี่เบรค#90.  4 เสาหลักของการเขียนโปรแกรมเชิงวัตถุ - 1OOP มี 4 เสาหลัก:
  1. การห่อหุ้ม
  2. มรดก
  3. นามธรรม
  4. ความแตกต่าง
ตอนนี้เราจะพูดถึงแต่ละรายการพร้อมคำอธิบายสั้น ๆ และตัวอย่างโค้ดจริง

1. การห่อหุ้ม

เราทุกคนได้ศึกษาการห่อหุ้มเป็นการซ่อนองค์ประกอบข้อมูลและอนุญาตให้ผู้ใช้เข้าถึงข้อมูลโดยใช้วิธีสาธารณะ เราเรียกสิ่งเหล่านี้ว่า getters และ setters ทีนี้ เรามาลืมเรื่องนี้แล้วหาคำจำกัดความที่ง่ายกว่านี้กันดีกว่า การห่อหุ้มเป็นวิธีการจำกัดผู้ใช้จากการเปลี่ยนแปลงสมาชิกข้อมูลหรือตัวแปรคลาสโดยตรงเพื่อรักษาความสมบูรณ์ของข้อมูล เราจะทำเช่นนี้ได้อย่างไร? เราจำกัดการเข้าถึงตัวแปรโดยการสลับตัวแก้ไขการเข้าถึงเป็นแบบส่วนตัวและเปิดเผยวิธีการสาธารณะที่สามารถใช้เพื่อเข้าถึงข้อมูล ลองดูตัวอย่างที่เฉพาะเจาะจงด้านล่าง ซึ่งจะช่วยให้เราเข้าใจว่าเราสามารถใช้การห่อหุ้มเพื่อรักษาความสมบูรณ์ของข้อมูลได้อย่างไร ไม่มีการห่อหุ้ม:
/**
 * @author thegeekyasian.com
 */
public class Account {

  public double balance;

  public static void main(String[] args) {

  	Account theGeekyAsianAccount = new Account();

  	theGeekyAsianAccount.balance = -54;
  }
}
ในข้อมูลโค้ดด้านบน เมธอด main()จะเข้าถึง ตัวแปร balanceโดยตรง ซึ่งช่วยให้ผู้ใช้สามารถตั้งค่าสองเท่าให้กับ ตัวแปรยอด คงเหลือ ของ คลาสบัญชีได้ เราอาจสูญเสียความสมบูรณ์ของข้อมูลได้โดยการอนุญาตให้ใครก็ตามกำหนดยอดคงเหลือให้กับตัวเลขที่ไม่ถูกต้อง เช่น -54 ในกรณีนี้ ด้วยการห่อหุ้ม:
/**
 * @author thegeekyasian.com
 */
public class Account {

  private double balance;

  public void setBalance(double balance) {

    if(balance >= 0) { // Validating input data in order to maintain data integrity
	  this.balance = balance;
    }

    throw new IllegalArgumentException("Balance cannot be less than zero (0)");
  }

  public static void main(String[] args) {

  	Account theGeekyAsianAccount = new Account();

  	theGeekyAsianAccount.setBalance(1); // Valid input - Allowed
  	theGeekyAsianAccount.setBalance(-55); // Stops user and throws exception
  }
}
ในโค้ดนี้ เราได้จำกัดการเข้าถึง ตัวแปร balanceและเพิ่ม เมธอด setBalance()ที่ให้ผู้ใช้สามารถตั้ง ค่า สมดุลสำหรับAccountได้ ผู้ตั้งค่าจะตรวจสอบค่าที่ให้มาก่อนที่จะกำหนดให้กับตัวแปร หากค่าน้อยกว่าศูนย์ ข้อยกเว้นจะเกิดขึ้น สิ่งนี้ทำให้แน่ใจได้ว่าความสมบูรณ์ของข้อมูลจะไม่ถูกทำลาย หลังจากอธิบายตัวอย่างข้างต้นแล้ว ฉันหวังว่าคุณค่าของการห่อหุ้มซึ่งเป็นหนึ่งในสี่เสาหลักของ OOP นั้นชัดเจน

2. มรดก

การสืบทอดเป็นวิธีการรับคุณสมบัติของคลาสอื่นที่แชร์คุณสมบัติทั่วไป สิ่งนี้ช่วยให้เราเพิ่มความสามารถในการใช้ซ้ำและลดความซ้ำซ้อนของโค้ด วิธีการนี้ยังมีหลักการของการโต้ตอบระหว่างเด็กและผู้ปกครอง เมื่อองค์ประกอบลูกสืบทอดคุณสมบัติของผู้ปกครอง มาเจาะลึกสองตัวอย่างสั้นๆ และดูว่าการสืบทอดทำให้โค้ดง่ายขึ้นและนำกลับมาใช้ใหม่ได้อย่างไร โดยไม่มีมรดก:
/**
 * @author thegeekyasian
 */
public class Rectangle {

  private int width;
  private int height;

  public Rectangle(int width, int height) {
	this.width = width;
	this.height = height;
  }

  public int getArea() {
	return width * height;
  }
}

public class Square {

  private int width; // Duplicate property, also used in class Rectangle

  public Square(int width) {
	this.width = width;
  }

  public int getArea() { // Duplicate method, similar to the class Rectangle
	return this.width * this.width;
  }
}
ทั้งสองคลาสที่คล้ายกันจะแชร์คุณสมบัติความกว้างและ เมธอด getArea() เราสามารถเพิ่มการนำโค้ดกลับ มาใช้ใหม่ได้โดยการปรับโครงสร้างใหม่เล็กน้อย โดยที่ คลาส Squareจะสืบทอดมาจาก คลาส สี่เหลี่ยมผืนผ้า ด้วยมรดก:
/**
 * @author thegeekyasian
 */
public class Rectangle {

  private int width;
  private int height;

  public Rectangle(int width, int height) {
	this.width = width;
	this.height = height;
  }

  public int getArea() {
	return width * height;
  }
}

public class Square extends Rectangle {

  public Square(int width) {
	super(width, width); // A rectangle with the same height as width is a square
  }
}
เพียงขยาย คลาส สี่เหลี่ยมผืนผ้าเราก็จะได้คลาสSquareเป็น ประเภท สี่เหลี่ยมผืนผ้า ซึ่งหมายความว่าจะสืบทอดคุณสมบัติทั้งหมดที่เหมือนกันกับSquareและสี่เหลี่ยมผืนผ้า ในตัวอย่างข้างต้น เราจะเห็นว่าการสืบทอดมีบทบาทสำคัญในการทำให้โค้ดสามารถนำมาใช้ซ้ำได้อย่างไร นอกจากนี้ยังอนุญาตให้คลาสสืบทอดพฤติกรรมของคลาสพาเรนต์ด้วย

3. สิ่งที่เป็นนามธรรม

นามธรรมเป็นเทคนิคในการนำเสนอเฉพาะรายละเอียดที่จำเป็นแก่ผู้ใช้โดยการซ่อนรายละเอียดที่ไม่จำเป็นหรือไม่เกี่ยวข้องของวัตถุ ช่วยลดความซับซ้อนในการปฏิบัติงานทางฝั่งผู้ใช้ Abstraction ช่วยให้เราสามารถจัดเตรียมอินเทอร์เฟซที่เรียบง่ายให้กับผู้ใช้โดยไม่ต้องขอรายละเอียดที่ซับซ้อนในการดำเนินการ พูดง่ายๆ ก็คือทำให้ผู้ใช้สามารถขับรถได้โดยไม่จำเป็นต้องเข้าใจว่าเครื่องยนต์ทำงานอย่างไร ลองดูตัวอย่างก่อนแล้วจึงอภิปรายว่านามธรรมช่วยเราได้อย่างไร
/**
* @author thegeekyasian.com
*/
public class Car {

  public void lock() {}
  public void unlock() {}

  public void startCar() {

	checkFuel();
	checkBattery();
	whatHappensWhenTheCarStarts();
  }

  private void checkFuel() {
	// Check fuel level
  }

  private void checkBattery() {
	// Check car battery
  }

  private void whatHappensWhenTheCarStarts() {
	// Magic happens here
  }
}
ในโค้ดข้างต้น วิธีการ lock() , Unlock()และstartCar()เป็นแบบสาธารณะ และส่วนที่เหลือเป็นแบบส่วนตัวในชั้นเรียน เราได้ทำให้ผู้ใช้สามารถ "ขับรถ" ได้ง่ายขึ้น แน่นอนว่าเขาสามารถตรวจสอบcheckFuel()และcheckBattery() ได้ด้วยตนเอง ก่อนสตาร์ทรถด้วย startCar()แต่นั่นจะทำให้กระบวนการซับซ้อนขึ้น ด้วยโค้ดข้างต้น สิ่งที่ผู้ใช้ต้องทำคือใช้startCar()และคลาสจะจัดการส่วนที่เหลือ นี่คือสิ่งที่เราเรียกว่านามธรรม

4. ความแตกต่าง

เสาหลักสุดท้ายและสำคัญที่สุดในสี่เสาหลักของ OOP คือความหลากหลาย Polymorphism หมายถึง "หลายรูปแบบ" ตามชื่อของมัน มันเป็นฟังก์ชั่นที่ให้คุณดำเนินการได้หลายวิธีหรือต่างกัน เมื่อเราพูดถึงความหลากหลาย ไม่มีอะไรให้พูดคุยมากนักเว้นแต่เราจะพูดถึงประเภทของมัน ความหลากหลายมีสองประเภท:
  1. วิธีการโอเวอร์โหลด - ความหลากหลายแบบคงที่ (Static Binding)
  2. การเอาชนะวิธีการ - ความหลากหลายแบบไดนามิก (Dynamic Binding)
มาหารือเกี่ยวกับแต่ละประเภทเหล่านี้และดูว่าความแตกต่างระหว่างพวกเขาคืออะไร

วิธีการโอเวอร์โหลด - ความหลากหลายแบบคงที่:

Method Overloading หรือ Static Polymorphism หรือที่เรียกว่า Static Binding หรือ Compile-time Binding เป็นประเภทที่กำหนดการเรียกเมธอด ณ เวลาคอมไพล์ วิธีการโอเวอร์โหลดช่วยให้เรามีหลายวิธีที่มีชื่อเดียวกัน มีประเภทข้อมูลพารามิเตอร์ที่แตกต่างกัน หรือจำนวนพารามิเตอร์ต่างกัน หรือทั้งสองอย่าง แต่คำถามก็คือ เหตุใดวิธีการโอเวอร์โหลด (หรือความหลากหลายแบบคงที่) จึงมีประโยชน์ ลองดูตัวอย่างด้านล่างเพื่อทำความเข้าใจวิธีการโอเวอร์โหลดให้ดียิ่งขึ้น โดยไม่ต้องโอเวอร์โหลดวิธีการ:
/**
* @author thegeekyasian.com
*/
public class Number {

  public void sumInt(int a, int b) {
	System.out.println("Sum: " + (a + b));
  }

  public void sumDouble(double a, double b) {
	System.out.println("Sum: " + (a + b));
  }

  public static void main(String[] args) {

	Number number = new Number();

	number.sumInt(1, 2);
	number.sumDouble(1.8, 2.5);
  }
}
ในตัวอย่างข้างต้น เราสร้างสองวิธีโดยใช้ชื่อที่แตกต่างกัน เพียงเพื่อเพิ่มตัวเลขสองประเภทที่แตกต่างกัน หากเราดำเนินการแบบเดียวกันต่อไป เราจะมีหลายวิธีที่มีชื่อต่างกัน ซึ่งจะลดคุณภาพและความพร้อมใช้งานของโค้ด เพื่อปรับปรุงสิ่งนี้ เราสามารถใช้วิธีโอเวอร์โหลดได้โดยใช้ชื่อเดียวกันสำหรับวิธีการต่างๆ ซึ่งจะทำให้ผู้ใช้สามารถมีทางเลือกเดียวเป็นจุดเริ่มต้นในการรวมตัวเลขประเภทต่างๆ การโอเวอร์โหลดเมธอดจะทำงานเมื่อเมธอดตั้งแต่สองเมธอดขึ้นไปมีชื่อเดียวกันแต่มีพารามิเตอร์ต่างกัน ประเภทการคืนสินค้าอาจเหมือนหรือต่างกันก็ได้ แต่ถ้าสองวิธีมีชื่อเหมือนกัน พารามิเตอร์เดียวกัน แต่มีประเภทการส่งคืนต่างกัน จะทำให้เกิดการโอเวอร์โหลดและข้อผิดพลาดในการคอมไพล์! ด้วยวิธีการโอเวอร์โหลด:
/**
* @author thegeekyasian.com
*/
public class Number {

  public void sum(int a, int b) {
	System.out.println("Sum: " + (a + b));
  }

  public void sum(double a, double b) {
	System.out.println("Sum: " + (a + b));
  }

  public static void main(String[] args) {

	Number number = new Number();

	number.sum(1, 2);
	number.sum(1.8, 2.5);
  }
}
ในโค้ดเดียวกัน มีการเปลี่ยนแปลงเล็กน้อยเล็กน้อย เราสามารถโอเวอร์โหลดทั้งสองวิธีได้ ทำให้ชื่อทั้งสองเหมือนกัน ขณะนี้ผู้ใช้สามารถระบุประเภทข้อมูลเฉพาะของตนเป็นพารามิเตอร์วิธีการได้ จากนั้นจะดำเนินการตามประเภทข้อมูลที่ให้ไว้ การผูกเมธอดนี้เสร็จสิ้นในเวลาคอมไพล์เนื่องจากคอมไพเลอร์รู้ว่าเมธอดใดที่จะถูกเรียกด้วยประเภทพารามิเตอร์ที่ระบุ นั่นเป็นเหตุผลที่เราเรียกมันว่าการผูกเวลาคอมไพล์

การเอาชนะวิธีการ - พหุสัณฐานแบบไดนามิก:

ไม่เหมือนกับการโอเวอร์โหลดเมธอด การแทนที่เมธอดทำให้คุณมีลายเซ็นเหมือนกันทุกประการกับเมธอดหลายวิธี แต่ต้องอยู่ในคลาสที่แตกต่างกันหลายคลาส คำถามก็คือ มีอะไรพิเศษเกี่ยวกับเรื่องนี้บ้าง? คลาสเหล่านี้มีความสัมพันธ์แบบ IS-A นั่นคือจะต้องสืบทอดจากกันและกัน กล่าวอีกนัยหนึ่ง ในการเอาชนะเมธอดหรือไดนามิกโพลีมอร์ฟิซึม เมธอดจะถูกประมวลผลแบบไดนามิกที่รันไทม์เมื่อมีการเรียกใช้เมธอด สิ่งนี้เสร็จสิ้นตามการอ้างอิงไปยังออบเจ็กต์ที่เริ่มต้น นี่เป็นตัวอย่างเล็กๆ น้อยๆ ของวิธีการเอาชนะ:
/**
* @author thegeekyasian.com
*/
public class Animal {

  public void walk() {
	System.out.println("Animal walks");
  }
}

public class Cat extends Animal {

  @Override
  public void walk() {
	System.out.println("Cat walks");
  }
}

public class Dog extends Animal {

  @Override
  public void walk() {
	System.out.println("Dog walks");
  }
}

public class Main {

  public static void main(String[] args) {

	Animal animal = new Animal();
	animal.walk(); // Animal walks

	Cat cat = new Cat();
	cat.walk(); // Cat walks

	Dog dog = new Dog();
	dog.walk(); // Dog walks

	Animal animalCat = new Cat(); // Dynamic Polymorphism
	animalCat.walk(); // Cat walks

	Animal animalDog = new Dog(); // Dynamic Polymorphism
	animalDog.walk(); //Dog walks
  }
}
ในตัวอย่างที่สำคัญนี้ เราได้กำหนดวัตถุประเภท "สุนัข" และ "แมว" ให้พิมพ์ "สัตว์" แบบไดนามิก สิ่งนี้ช่วยให้เราสามารถเรียกใช้เมธอดwalk()บนอินสแตนซ์ที่อ้างอิงแบบไดนามิกที่รันไทม์ เราสามารถทำได้โดยใช้วิธีการเอาชนะ (หรือความหลากหลายแบบไดนามิก) นี่เป็นการสรุปการสนทนาสั้น ๆ ของเราเกี่ยวกับเสาหลักทั้งสี่ของ OOP และฉันหวังว่าคุณจะพบว่ามีประโยชน์
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION