Interface ба abstract класс
Өвлөлт нь нэг эх классаас л өвлөж болдог хязгаартай. Гэвч бодит дэлхийд нэг объект олон үүрэг гүйцэтгэдэг — жишээлбэл, Нохой нь "Амьтан" мөн, "Гэрийн тэжээвэр" мөн. Interface нь яг энэ асуудлыг шийддэг: нэг класс хэдэн ч interface-г хэрэгжүүлж болно. Abstract класс нь interface болон жирийн классын дундах зүйл — зарим аргыг хэрэгжүүлж, зарим аргыг дэд классд үлдээдэг.
Interface — гэрээ тодорхойлох
interface нь аргуудын гарын үсэг (signature) тодорхойлдог — биелэлт агуулдаггүй. Классыг implements гэж хэрэгжүүлэхдээ бүх аргыг заавал бодитоор тодорхойлох ёстой:
<?php
interface Дуугарагч {
public function дуугарах(): string;
}
interface Хөдөлгөөнт {
public function хөдөлгөөн(): string;
public function зогсох(): void;
}
// Нэг класс хэдэн ч interface хэрэгжүүлж болно
class Нохой implements Дуугарагч, Хөдөлгөөнт {
public function дуугарах(): string {
return "Хав хав!";
}
public function хөдөлгөөн(): string {
return "Гүйж байна";
}
public function зогсох(): void {
echo "Нохой зогслоо.\n";
}
}
$нохой = new Нохой();
echo $нохой->дуугарах(); // Хав хав!
echo $нохой->хөдөлгөөн(); // Гүйж байна
$нохой->зогсох(); // Нохой зогслоо.
// instanceof шалгалт interface-тэй ч ажилладаг
echo ($нохой instanceof Дуугарагч) ? "Дуугарагч мөн\n" : "";
echo ($нохой instanceof Хөдөлгөөнт) ? "Хөдөлгөөнт мөн\n" : "";
?>
Interface-г хэрэгжүүлэхдээ нэг ч аргыг орхивол PHP алдаа шиддэг. Энэ нь "гэрээ" шиг ажилладаг — хэрэгжүүлсэн класс бүх үүргээ заавал биелүүлнэ.
Interface-г тип болгон ашиглах
Interface-ийн хамгийн том давуу тал нь функцийн параметр болгон ашиглах явдал юм. Ямар ч класс тэр interface-г хэрэгжүүлсэн л бол дамжуулж болно:
<?php
interface Төлбөр_Систем {
public function төлбөр_хийх(float $дүн): bool;
public function нэр(): string;
}
class QPay implements Төлбөр_Систем {
public function төлбөр_хийх(float $дүн): bool {
echo "QPay-р {$дүн}₮ төлбөр хийлээ.\n";
return true;
}
public function нэр(): string {
return "QPay";
}
}
class Карт implements Төлбөр_Систем {
public function төлбөр_хийх(float $дүн): bool {
echo "Картаар {$дүн}₮ төлбөр хийлээ.\n";
return true;
}
public function нэр(): string {
return "Банкны карт";
}
}
// Функц нь interface-г параметр болгон авна — аль ч хэрэгжүүлэлт ажиллана
function гүйлгээ_хийх(Төлбөр_Систем $систем, float $дүн): void {
if ($систем->төлбөр_хийх($дүн)) {
echo $систем->нэр() . " амжилттай.\n";
}
}
гүйлгээ_хийх(new QPay(), 15_000); // QPay-р 15000₮ төлбөр хийлээ.
гүйлгээ_хийх(new Карт(), 50_000); // Картаар 50000₮ төлбөр хийлээ.
?>
Шинэ төлбөрийн систем нэмэхэд гүйлгээ_хийх() функцийг огт өөрчлөхгүйгээр шинэ класс нэмэхэд хангалттай — энэ бол мэргэжлийн програмчлалын гол зарчим.
Abstract класс — дунд зам
abstract класс нь interface болон жирийн классын хооронд оршдог. Зарим аргыг бодитоор хэрэгжүүлж, зарим аргыг abstract гэж тэмдэглэн дэд классд үлдээж болно. abstract классаас шууд объект үүсгэж болохгүй:
<?php
abstract class Тайлан {
// Бодит арга — бүх дэд класс ашиглана
public function гарчиг(): void {
echo "=== " . $this->гарчиг_текст() . " ===\n";
}
public function хэвлэх(): void {
$this->гарчиг();
$this->агуулга(); // дэд класс хэрэгжүүлнэ
echo "\n--- Тайлан дуусав ---\n";
}
// Abstract арга — дэд класс заавал хэрэгжүүлэх ёстой
abstract protected function гарчиг_текст(): string;
abstract protected function агуулга(): void;
}
class Борлуулалтын_тайлан extends Тайлан {
public function __construct(private float $нийт) {}
protected function гарчиг_текст(): string {
return "Борлуулалтын тайлан";
}
protected function агуулга(): void {
echo "Нийт борлуулалт: " . number_format($this->нийт) . "₮\n";
}
}
class Хэрэглэгчийн_тайлан extends Тайлан {
public function __construct(private int $тоо) {}
protected function гарчиг_текст(): string {
return "Хэрэглэгчийн тайлан";
}
protected function агуулга(): void {
echo "Нийт хэрэглэгч: {$this->тоо}\n";
}
}
(new Борлуулалтын_тайлан(5_800_000))->хэвлэх();
(new Хэрэглэгчийн_тайлан(142))->хэвлэх();
// new Тайлан(); // АЛДАА — abstract класс шууд үүсгэж болохгүй
?>
Interface ба abstract — хэзээ алийг ашиглах вэ?
| | Interface | Abstract класс | |---|---|---| | Биелэлт агуулах | Үгүй | Тийм (зарим арга) | | Хэдийг хэрэгжүүлэх | Хэдийг ч | Зөвхөн нэгийг | | Шинж чанар (property) | Зөвхөн constant | Тийм | | Ашиглах тохиолдол | Үүрэг тодорхойлох | Нийтлэг суурь бүтээх |
<?php
// Interface — "юу хийх чадвартай" гэдгийг тодорхойлно
interface Экспортлох_Боломжтой {
public function csv_экспорт(): string;
public function json_экспорт(): string;
}
// Abstract класс — нийтлэг логикийг агуулна
abstract class Тооцоолол_Суурь {
abstract public function тооцоолох(): float;
// Нийтлэг форматлах логик
public function форматлах(): string {
return number_format($this->тооцоолох(), 2) . "₮";
}
}
// Хоёуланг хамт хэрэглэж болно
class НӨАТ_Тооцоолол extends Тооцоолол_Суурь implements Экспортлох_Боломжтой {
public function __construct(private float $үнэ) {}
public function тооцоолох(): float {
return $this->үнэ * 0.1; // 10% НӨАТ
}
public function csv_экспорт(): string {
return "үнэ,нөат\n{$this->үнэ}," . $this->тооцоолох();
}
public function json_экспорт(): string {
return json_encode(['үнэ' => $this->үнэ, 'нөат' => $this->тооцоолох()]);
}
}
$нөат = new НӨАТ_Тооцоолол(100_000);
echo $нөат->форматлах(); // 10,000.00₮
echo $нөат->csv_экспорт();
?>
Дараагийн хичээлд:
PHP-н өвөрмөц боломж болох trait-ийг судална. Trait нь нэг классаас л өвлөж болдог хязгаарыг тойрч, олон классд нийтлэг код хуваалцах боломж олгодог. "Хуулж оруулах" гэсэн утгаараа interface-ээс яагаад ялгаатай болохыг жишээгээр үзнэ.