ЛЕКЦ 02: ДИЗАЙН ПАТТЕРНУУД (Design Patterns)
Хичээлийн зорилго: Програм хангамжийн бүтээлтэд байнга давтагддаг асуудлуудыг шийдвэрлэх батлагдсан загваруудыг (design patterns) ойлгож, Java хэл дээр хэрэгжүүлэх чадвар эзэмших.
ХЭСЭГ 1: ҮНДСЭН ОЙЛГОЛТ БА ОНОЛ (Theory & Foundations)
1.1 Дизайн паттерн гэж юу вэ? (What is a Design Pattern?)
Тодорхойлолт
Дизайн паттерн (Design Pattern) гэдэг нь програм хангамжийн дизайнд байнга давтагддаг асуудлыг шийдвэрлэх батлагдсан загвар юм.
Gang of Four (GoF) номноос: "Дизайн паттерн гэдэг нь тодорхой контекст дэх ерөнхий дизайны асуудлыг шийдвэрлэхэд зориулсан, хоорондоо харилцах объект, классуудын тодорхойлолт юм."
Түүхэн мэдээлэл
Дизайн паттерн гэдэг ойлголт нь анх Кристофер Александер (Christopher Alexander) гэдэг архитекторын 1977 оны "A Pattern Language: Towns, Buildings, Construction" номноос үүдэлтэй. Тэрээр барилга, хот байгуулалтад байнга давтагддаг асуудлуудыг шийдвэрлэх загваруудыг баримтжуулсан.
1995 онд Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides нар Александерийн санааг програм хангамжийн ертөнцөд авчирч "Design Patterns: Elements of Reusable Object-Oriented Software" номыг хэвлэсэн. Энэ 4 зохиогчийг "Gang of Four" (GoF) буюу "Дөрвөн хүний бүлэг" гэж нэрлэдэг.
Амьдрал дээрх зүйрлэл
Зүйрлэл 1 — Хоолны жор: Тогооч хоол бүрийг эхнээс нь зохион бодохгүй. Үүний оронд батлагдсан жор ашигладаг. Жор нь "ямар асуудлыг (юу хоол хийх), ямар орцоор, ямар дарааллаар шийдвэрлэх" гэдгийг тодорхойлдог. Дизайн паттерн ч яг ийм — програмчлалын "жор".
Зүйрлэл 2 — Барилгын зураг төсөл: Архитектор байшин бүрийг тэг-ээс зохиохгүй. "3 өрөө байр", "гал тогооны стандарт зохион байгуулалт" гэх мэт батлагдсан загварууд байдаг. Дизайн паттерн нь програмын "архитектурын загвар" юм.
Паттерн яагаад чухал вэ?
- Батлагдсан шийдэл — Олон жилийн туршлагаар шалгагдсан, алдаа бага
- Нийтлэг хэл — Хөгжүүлэгчид хоорондоо "Энд Singleton хэрэглэ" гэхэд бүгд ойлгоно
- Дахин ашиглалт — Нэг удаа сурч, олон удаа ашиглана
- Код уншигдахуйц — Паттерн мэддэг хүн кодыг амархан ойлгоно
- Уян хатан дизайн — Өөрчлөлтөд нээлттэй, засвар хялбар
Паттерны 3 бүрэлдэхүүн хэсэг
Паттерн бүр гурван зүйлийг тодорхойлдог:
| Бүрэлдэхүүн | Тайлбар | Зүйрлэл |
|---|---|---|
| Асуудал (Problem) | Ямар нөхцөлд хэрэглэх вэ? | "Хоол амтгүй байна, юу хийх вэ?" |
| Контекст (Context) | Ямар нөхцөл байдалд хэрэглэх вэ? | "Монгол зочин байна, Монгол хоол хийх хэрэгтэй" |
| Шийдэл (Solution) | Хэрхэн шийдвэрлэх вэ? | "Бууз хийх жор: гурил, мах, сонгино..." |
1.2 Паттерны 3 том ангилал (Three Categories of Patterns)
GoF 23 паттерныг 3 том бүлэгт хуваадаг:
А) Бүтээх паттернууд (Creational Patterns) — 5 ш
Объект ЯАЖЕ ҮҮСГЭХ-тэй холбоотой. Объект үүсгэх процессыг хийсвэрлэж, системийг объект яаж үүссэнээс хамааралгүй болгодог.
Зүйрлэл: Автомашины үйлдвэр — захиалагч "Би седан авах" гэнэ. Үйлдвэр доторх нарийн үйл явцыг мэдэх шаардлагагүй — зүгээр л машин гарч ирнэ.
| # | Паттерн | Товч тайлбар |
|---|---|---|
| 1 | Singleton | Зөвхөн НЭГ объект үүсгэж, бүх газраас хандах |
| 2 | Factory Method | Объект үүсгэхийг дэд класст шилжүүлэх |
| 3 | Abstract Factory | Хамааралтай объектуудын гэр бүлийг нэг дор үүсгэх |
| 4 | Builder | Нарийн төвөгтэй объектыг алхам алхмаар бүтээх |
| 5 | Prototype | Одоо байгаа объектыг хуулбарлаж шинэ объект үүсгэх |
Б) Бүтцийн паттернууд (Structural Patterns) — 7 ш
Класс, объектуудыг ЯАЖЕ НЭГТГЭХ-тэй холбоотой. Объектуудыг том бүтцэд хэрхэн зохион байгуулахыг тодорхойлдог.
Зүйрлэл: LEGO — жижиг хэсгүүдийг хоорондоо холбож том бүтэц бүтээх. Хэсэг бүр өөрөө ч ажилладаг, бусадтай холбогдсон ч ажилладаг.
| # | Паттерн | Товч тайлбар |
|---|---|---|
| 1 | Adapter | Хоёр нийцдэггүй интерфейсийг холбох |
| 2 | Bridge | Хийсвэрлэлийг хэрэгжүүлэлтээс салгах |
| 3 | Composite | Объектуудыг модлог (tree) бүтцэд зохион байгуулах |
| 4 | Decorator | Объектод шинэ функц динамикаар нэмэх |
| 5 | Facade | Нарийн системд хялбар интерфейс хангах |
| 6 | Flyweight | Олон тооны ижил объектын санах ойг хэмнэх |
| 7 | Proxy | Өөр объектыг орлох, хандалтыг хянах |
В) Зан төлөвийн паттернууд (Behavioral Patterns) — 11 ш
Объектууд ЯАЖЕ ХАРИЛЦАХ-тай холбоотой. Алгоритм, хариуцлагыг хуваарилах аргуудыг тодорхойлдог.
Зүйрлэл: Хөл бөмбөгийн баг — тоглогч бүр өөрийн үүрэгтэй (хамгаалагч, дунд, довтлогч) бөгөөд тэд хоорондоо дохио, дамжуулалтаар харилцдаг. Паттерн нь энэ "харилцааны дүрмийг" тодорхойлдог.
| # | Паттерн | Товч тайлбар |
|---|---|---|
| 1 | Observer | Объектын төлөв өөрчлөгдөхөд бусдад мэдэгдэх |
| 2 | Strategy | Алгоритмыг сольж болдог болгох |
| 3 | Command | Хүсэлтийг объект болгож капсулжуулах |
| 4 | State | Объект төлөвөөс хамааран зан төлөвөө өөрчлөх |
| 5 | Template Method | Алгоритмын араг ясыг тодорхойлж, дэд класст нарийн хэсгийг шилжүүлэх |
| 6 | Iterator | Цуглуулгын элементүүдийг дараалуулан хандах |
| 7 | Mediator | Объектуудын харилцааг нэг цэгт төвлөрүүлэх |
| 8 | Memento | Объектын өмнөх төлөвийг хадгалж, сэргээх |
| 9 | Chain of Responsibility | Хүсэлтийг гинжин хэлхээгээр дамжуулах |
| 10 | Visitor | Бүтцээ өөрчлөхгүйгээр шинэ үйлдэл нэмэх |
| 11 | Interpreter | Хэлний дүрэм тодорхойлж, тайлбарлах |
1.3 Бүтээх паттернууд дэлгэрэнгүй (Creational Patterns in Detail)
1.3.1 Singleton — Ганцхан объект
Асуудал: Зарим тохиолдолд класснаас зөвхөн нэг объект үүсэх ёстой. Жишээ нь: мэдээллийн сангийн холболт, тохиргооны менежер, лог бичигч.
Зүйрлэл: Монгол улсад Ерөнхийлөгч ганцхан байдаг. Хэн ч шинээр Ерөнхийлөгч "үүсгэж" чадахгүй — одоо байгаа Ерөнхийлөгчийг л авч ашиглана.
Шийдэл:
- Constructor-ийг
privateболгох → гаднаасnewхийж чадахгүй - Статик
getInstance()метод хангах → Нэг л объект буцаана
public class DatabaseConnection {
// 1. Ганцхан instance-ийг static талбарт хадгалах
private static DatabaseConnection instance;
// 2. Constructor-ийг private болгох — гаднаас үүсгэж чадахгүй
private DatabaseConnection() {
System.out.println("Мэдээллийн сантай холбогдлоо!");
}
// 3. Ганцхан instance-д хандах цорын ганц арга
public static DatabaseConnection getInstance() {
if (instance == null) { // Хэрэв instance үүсээгүй бол
instance = new DatabaseConnection(); // Үүсгэнэ
}
return instance; // Байгаа instance-ийг буцаана
}
public void query(String sql) {
System.out.println("SQL ажиллууллаа: " + sql);
}
}
// Хэрэглээ:
// DatabaseConnection db1 = DatabaseConnection.getInstance();
// DatabaseConnection db2 = DatabaseConnection.getInstance();
// db1 == db2 → true (яг адилхан объект!)
Хэзээ ашиглах: Мэдээллийн сангийн холболт, Logger, Configuration, Thread pool
1.3.2 Factory Method — Үйлдвэрийн арга
Асуудал: Ямар класснаас объект үүсгэхийг код ажиллаж байх үед шийдэх шаардлагатай. Шинэ төрөл нэмэхэд одоо байгаа кодыг өөрчлөхгүй байх.
Зүйрлэл: Пиццаны дэлгүүр — Захиалагч "Пепперони пицца" гэнэ. Дэлгүүр дотор ЯАЖЕ бэлтгэхийг (овен, температур, хугацаа) мэдэх шаардлагагүй. Дэлгүүр (Factory) бэлэн пицца (Object) гаргаж өгнө.
// Бүтээгдэхүүний интерфейс
interface Notification {
void send(String message);
}
// Бодит бүтээгдэхүүнүүд
class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("📧 Email илгээлээ: " + message);
}
}
class SMSNotification implements Notification {
@Override
public void send(String message) {
System.out.println("📱 SMS илгээлээ: " + message);
}
}
class PushNotification implements Notification {
@Override
public void send(String message) {
System.out.println("🔔 Push мэдэгдэл: " + message);
}
}
// Factory — Объект үүсгэх үйлдвэр
class NotificationFactory {
public static Notification create(String type) {
switch (type.toLowerCase()) {
case "email": return new EmailNotification();
case "sms": return new SMSNotification();
case "push": return new PushNotification();
default: throw new IllegalArgumentException("Тодорхойгүй төрөл: " + type);
}
}
}
// Хэрэглээ:
// Notification notif = NotificationFactory.create("email");
// notif.send("Сайн байна уу!");
// → 📧 Email илгээлээ: Сайн байна уу!
Давуу тал: Шинэ төрөл нэмэхэд зөвхөн Factory-д нэг case нэмнэ → Бусад код өөрчлөгдөхгүй
1.3.3 Builder — Алхам алхмаар бүтээгч
Асуудал: Олон параметртэй нарийн төвөгтэй объект үүсгэх. Constructor-т 10+ параметр дамжуулах нь ойлгомжгүй.
Зүйрлэл: Бургер захиалах — "Том талх, давхар мах, бяслаг тийм, лоолийн соус, сонгиногүй" гэж алхам алхмаар хэлдэг. Бүгдийг нэг дор хэлэх шаардлагагүй.
public class Student {
private String name; // Заавал
private int age; // Заавал
private String email; // Нэмэлт
private String phone; // Нэмэлт
private String address; // Нэмэлт
// Private constructor — зөвхөн Builder-ээр үүсгэнэ
private Student(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.email = builder.email;
this.phone = builder.phone;
this.address = builder.address;
}
// Дотоод Builder класс
public static class Builder {
private String name; // Заавал
private int age; // Заавал
private String email; // Нэмэлт
private String phone; // Нэмэлт
private String address; // Нэмэлт
public Builder(String name, int age) { // Заавал талбарууд
this.name = name;
this.age = age;
}
public Builder email(String email) { // Нэмэлт талбарууд
this.email = email;
return this; // this буцааж chain хийх
}
public Builder phone(String phone) {
this.phone = phone;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Student build() { // Эцсийн объект үүсгэх
return new Student(this);
}
}
@Override
public String toString() {
return "Оюутан: " + name + ", " + age + " нас"
+ (email != null ? ", " + email : "")
+ (phone != null ? ", " + phone : "");
}
}
// Хэрэглээ — уншихад маш ойлгомжтой:
// Student student = new Student.Builder("Бат", 20)
// .email("bat@email.com")
// .phone("99112233")
// .build();
1.3.4 Prototype — Хуулбарлагч
Асуудал: Объект үүсгэх нь нөөц их шаарддаг (мэдээллийн сангаас уншдаг, тооцоолол хийдэг). Одоо байгаа объектыг хуулбарлах нь хурдан.
Зүйрлэл: Ажлын байрны хүсэлтийн маягт — Эхний хүсэлтийг анхнаас бөглөнө. Дараагийнхийг ХУУЛБАРЛАЖ, өөрчлөх зүйлээ л засна.
public class Document implements Cloneable {
private String title;
private String content;
public Document(String title, String content) {
this.title = title;
this.content = content;
}
// Prototype — clone() метод
@Override
public Document clone() {
try {
return (Document) super.clone(); // Хуулбар үүсгэх
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public void setTitle(String title) { this.title = title; }
public String toString() { return title + ": " + content; }
}
// Хэрэглээ:
// Document original = new Document("Гэрээ", "Энэ бол гэрээний агуулга...");
// Document copy = original.clone(); // Хуулбарлах
// copy.setTitle("Гэрээ - Хувилбар 2"); // Зөвхөн нэрийг өөрчлөх
1.4 Бүтцийн паттернууд дэлгэрэнгүй (Structural Patterns in Detail)
1.4.1 Adapter — Тохируулагч
Асуудал: Хоёр класс хоорондоо нийцдэггүй интерфейстэй ч хамт ажиллах шаардлагатай.
Зүйрлэл: Гадаадад аялахад залгуурын хэлбэр өөр байдаг. Adapter (залгуурын хувиргагч) ашиглаж Монгол залгуурыг Европын розеткад тохируулдаг шиг.
// Хуучин систем — зөвхөн XML мэддэг
class OldAnalytics {
public void analyzeXML(String xmlData) {
System.out.println("XML шинжилгээ: " + xmlData);
}
}
// Шинэ систем — JSON ашигладаг
interface ModernAnalytics {
void analyzeJSON(String jsonData);
}
// Adapter — Хуучин системийг шинэ интерфейсээр ашиглуулна
class AnalyticsAdapter implements ModernAnalytics {
private OldAnalytics oldAnalytics;
public AnalyticsAdapter(OldAnalytics oldAnalytics) {
this.oldAnalytics = oldAnalytics;
}
@Override
public void analyzeJSON(String jsonData) {
// JSON-ийг XML руу хувиргаж, хуучин системд дамжуулна
String xmlData = convertJsonToXml(jsonData);
oldAnalytics.analyzeXML(xmlData);
}
private String convertJsonToXml(String json) {
return "<data>" + json + "</data>"; // Хялбаршуулсан хувиргалт
}
}
1.4.2 Facade — Нүүр хаалга
Асуудал: Нарийн төвөгтэй системтэй харилцахад олон класс, метод дуудах шаардлагатай.
Зүйрлэл: Зочид буудлын ресепшн — Та ресепшнд л хэлнэ, тэд цэвэрлэгээ, хоол, такси бүгдийг зохион байгуулна. Та хоол хийгчтэй, жолоочтой шууд ярилцах шаардлагагүй.
// Нарийн төвөгтэй дэд системүүд
class VideoFile { /* видео файл уншигч */ }
class AudioExtractor { public void extract() { System.out.println("Аудио задлав"); } }
class VideoCompressor { public void compress() { System.out.println("Видео шахав"); } }
class CodecFactory { public void getCodec() { System.out.println("Кодек сонгов"); } }
// Facade — Нэг энгийн интерфейс
class VideoConverter {
public void convert(String filename, String format) {
System.out.println("=== " + filename + " → " + format + " руу хөрвүүлж байна ===");
new CodecFactory().getCodec();
new AudioExtractor().extract();
new VideoCompressor().compress();
System.out.println("=== Хөрвүүлэлт амжилттай! ===");
}
}
// Хэрэглээ — маш энгийн:
// VideoConverter converter = new VideoConverter();
// converter.convert("movie.avi", "mp4");
1.4.3 Decorator — Чимэглэгч
Асуудал: Объектод ажиллах үед шинэ функц динамикаар нэмэх. Удамшилаар шийдвэл класс тэсрэлт (class explosion) үүснэ.
Зүйрлэл: Кофе — Энгийн кофе + сүү = сүүтэй кофе. Сүүтэй кофе + чихэр = чихэртэй сүүтэй кофе. Хэрэглэгч хүссэн нэмэлтүүдээ давхарлаж "чимэглэнэ".
// Суурь интерфейс
interface Coffee {
double getCost();
String getDescription();
}
// Суурь бүтээгдэхүүн
class SimpleCoffee implements Coffee {
@Override
public double getCost() { return 2000; }
@Override
public String getDescription() { return "Энгийн кофе"; }
}
// Decorator суурь класс
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; }
}
// Бодит decorator-ууд
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) { super(coffee); }
@Override
public double getCost() { return coffee.getCost() + 500; }
@Override
public String getDescription() { return coffee.getDescription() + " + Сүү"; }
}
class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) { super(coffee); }
@Override
public double getCost() { return coffee.getCost() + 200; }
@Override
public String getDescription() { return coffee.getDescription() + " + Чихэр"; }
}
// Хэрэглээ:
// Coffee coffee = new SimpleCoffee(); // Энгийн кофе: 2000₮
// coffee = new MilkDecorator(coffee); // + Сүү: 2500₮
// coffee = new SugarDecorator(coffee); // + Чихэр: 2700₮
// System.out.println(coffee.getDescription() + " = " + coffee.getCost() + "₮");
// → "Энгийн кофе + Сүү + Чихэр = 2700₮"
1.4.4 Proxy — Орлогч
Асуудал: Объектод хандахыг хянах, хойшлуулах, эсвэл нэмэлт логик нэмэх шаардлагатай.
Зүйрлэл: Нарийн бичгийн дарга — Та захиралтай уулзахын тулд нарийн бичгийн даргаар дамжина. Тэр таны цагийг товлох, хандалтыг шалгах, захиралд мэдэгдэх зэргийг хийнэ.
interface Image {
void display();
}
// Бодит объект — Их нөөц шаарддаг
class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk(); // Их цаг зарцуулдаг
}
private void loadFromDisk() {
System.out.println("Дискнээс ачааллаж байна: " + filename);
}
@Override
public void display() {
System.out.println("Зураг харуулж байна: " + filename);
}
}
// Proxy — Бодит объектын орлогч
class ProxyImage implements Image {
private String filename;
private RealImage realImage; // Lazy loading
public ProxyImage(String filename) {
this.filename = filename; // Зүгээр нэрийг хадгална
}
@Override
public void display() {
if (realImage == null) { // Анх удаа дуудахад л ачаална
realImage = new RealImage(filename);
}
realImage.display();
}
}
1.5 Зан төлөвийн паттернууд дэлгэрэнгүй (Behavioral Patterns in Detail)
1.5.1 Observer — Ажиглагч
Асуудал: Нэг объектын төлөв өөрчлөгдөхөд олон объектод автоматаар мэдэгдэх шаардлагатай.
Зүйрлэл: YouTube суваг дагах (subscribe) — Шинэ видео нийтлэгдэхэд бүх дагагчдад мэдэгдэл очдог. Дагагч нэмэх, хасах нь сувагт нөлөөлөхгүй.
import java.util.ArrayList;
import java.util.List;
// Observer интерфейс — Мэдэгдэл хүлээн авагч
interface Subscriber {
void update(String channelName, String videoTitle);
}
// Subject — Мэдэгдэл илгээгч (YouTube суваг)
class YouTubeChannel {
private String name;
private List<Subscriber> subscribers = new ArrayList<>();
public YouTubeChannel(String name) { this.name = name; }
public void subscribe(Subscriber sub) {
subscribers.add(sub);
System.out.println("✅ Шинэ дагагч нэмэгдлээ!");
}
public void unsubscribe(Subscriber sub) {
subscribers.remove(sub);
}
// Шинэ видео нийтлэхэд БҮХ дагагчдад мэдэгдэнэ
public void publishVideo(String title) {
System.out.println("🎬 Шинэ видео: " + title);
for (Subscriber sub : subscribers) {
sub.update(name, title); // Бүх дагагчид мэдэгдэх
}
}
}
// Бодит Observer
class User implements Subscriber {
private String name;
public User(String name) { this.name = name; }
@Override
public void update(String channelName, String videoTitle) {
System.out.println("🔔 " + name + " → " + channelName + ": \"" + videoTitle + "\"");
}
}
// Хэрэглээ:
// YouTubeChannel channel = new YouTubeChannel("TechMN");
// channel.subscribe(new User("Бат"));
// channel.subscribe(new User("Болд"));
// channel.publishVideo("Java Design Patterns");
// → 🔔 Бат → TechMN: "Java Design Patterns"
// → 🔔 Болд → TechMN: "Java Design Patterns"
1.5.2 Strategy — Стратеги
Асуудал: Нэг ажлыг олон өөр аргаар хийж болох бөгөөд ажиллах үед аргаа сольж болох ёстой.
Зүйрлэл: Ажил руугаа явах — Автобусаар, таксиар, алхаж, дугуйгаар... Зорилго нэг (ажил руу хүрэх), арга олон. Цаг агаар, мөнгөнөөсөө хамааран аргаа сонгоно.
// Strategy интерфейс
interface PaymentStrategy {
void pay(int amount);
}
// Бодит стратегиуд
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
public CreditCardPayment(String cardNumber) { this.cardNumber = cardNumber; }
@Override
public void pay(int amount) {
System.out.println("💳 Картаар төлөв: " + amount + "₮ (Карт: " + cardNumber + ")");
}
}
class QPayPayment implements PaymentStrategy {
private String phoneNumber;
public QPayPayment(String phoneNumber) { this.phoneNumber = phoneNumber; }
@Override
public void pay(int amount) {
System.out.println("📱 QPay-ээр төлөв: " + amount + "₮ (Утас: " + phoneNumber + ")");
}
}
class CashPayment implements PaymentStrategy {
@Override
public void pay(int amount) {
System.out.println("💵 Бэлнээр төлөв: " + amount + "₮");
}
}
// Context — Стратегийг ашиглагч
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy; // Стратегийг солих
}
public void checkout(int amount) {
paymentStrategy.pay(amount); // Сонгосон стратегиар төлөх
}
}
// Хэрэглээ:
// ShoppingCart cart = new ShoppingCart();
// cart.setPaymentStrategy(new QPayPayment("99112233"));
// cart.checkout(50000);
// → 📱 QPay-ээр төлөв: 50000₮ (Утас: 99112233)
1.5.3 Template Method — Загвар арга
Асуудал: Алгоритмын ерөнхий бүтэц ижил, гэхдээ зарим алхмууд өөр өөр.
Зүйрлэл: Хоол хийх ерөнхий дараалал — (1) орцыг бэлтгэ, (2) хоолоо хий, (3) таваглаж өг. Бууз ч, хуушуур ч, банш ч энэ дарааллаар хийгдэнэ — зөвхөн нарийн алхмууд нь өөр.
// Template Method — Ерөнхий араг яс
abstract class MealPreparation {
// Template method — дарааллыг тодорхойлно (final = дэд класс өөрчилж чадахгүй)
public final void prepareMeal() {
prepareIngredients();
cook();
serve();
}
abstract void prepareIngredients(); // Дэд класс тодорхойлно
abstract void cook(); // Дэд класс тодорхойлно
void serve() { // Ерөнхий хэрэгжүүлэлт
System.out.println("🍽️ Таваглаж өгөв!");
}
}
class BuuzPreparation extends MealPreparation {
@Override
void prepareIngredients() {
System.out.println("🥟 Гурил зуурч, мах бэлтгэв");
}
@Override
void cook() {
System.out.println("♨️ Жигнэж байна... 20 минут");
}
}
class PizzaPreparation extends MealPreparation {
@Override
void prepareIngredients() {
System.out.println("🍕 Гурил дэлгэж, соус, бяслаг тавив");
}
@Override
void cook() {
System.out.println("🔥 Зууханд шаржигнуулж байна... 15 минут");
}
}
1.5.4 Command — Команд
Асуудал: Хүсэлтийг объект болгож хадгалах, дараалалд оруулах, буцаах (undo) шаардлагатай.
Зүйрлэл: Ресторанд захиалга өгөх — Зөөгч таны захиалгыг цаасан дээр бичиж (Command объект), гал тогоонд дамжуулна. Захиалгыг цуцлах (undo), дараалалд оруулах боломжтой.
// Command интерфейс
interface Command {
void execute();
void undo();
}
// Receiver — Бодит ажлыг гүйцэтгэгч
class Light {
public void turnOn() { System.out.println("💡 Гэрэл асав!"); }
public void turnOff() { System.out.println("🌑 Гэрэл унтрав!"); }
}
// Бодит Command-ууд
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) { this.light = light; }
@Override
public void execute() { light.turnOn(); }
@Override
public void undo() { light.turnOff(); }
}
// Invoker — Command-ыг ажиллуулагч
class RemoteControl {
private Command lastCommand;
public void pressButton(Command command) {
command.execute();
lastCommand = command;
}
public void pressUndo() {
if (lastCommand != null) {
lastCommand.undo();
}
}
}
1.6 Паттерн хэрэглэх зарчмууд (When and How to Use Patterns)
Хэрэглэх ёстой үед:
- Давтагддаг асуудал илэрсэн үед
- Уян хатан дизайн шаардлагатай үед
- Багийн харилцааг сайжруулах үед
Хэрэглэх ёсгүй үед:
- Хэт энгийн асуудалд паттерн хэрэглэх нь over-engineering
- Паттерн нь кодын нарийн төвөгтэй байдлыг нэмдэг — зөвхөн шаардлагатай үед ашиглах
- Паттерн хэрэглэх гэж хэрэглэхгүй — Асуудлаа тодорхой ойлгоод, паттерн тохирох эсэхийг шалгах
"When Not to Use Design Patterns" — GoF номноос: "Хамгийн энгийн шийдэл нь хамгийн сайн шийдэл. Паттерн нь зөвхөн нарийн төвөгтэй байдал зөвтгөгдөх үед ашиглагдах ёстой."
SOLID зарчмууд ба Design Patterns
Дизайн паттернууд нь SOLID зарчмуудыг хэрэгжүүлэхэд тусалдаг:
| SOLID зарчим | Тайлбар | Холбоотой паттерн |
|---|---|---|
| S — Single Responsibility | Нэг класс нэг л үүрэгтэй | Command, Strategy |
| O — Open/Closed | Өргөтгөхөд нээлттэй, өөрчлөхөд хаалттай | Decorator, Observer |
| L — Liskov Substitution | Дэд класс эцэг классыг орлож чадах | Factory, Template Method |
| I — Interface Segregation | Интерфейсийг жижиглэх | Adapter, Facade |
| D — Dependency Inversion | Хийсвэрлэлээс хамаарах, бодит хэрэгжүүлэлтээс биш | Factory, Strategy |
ХЭСЭГ 2: ТҮЛХҮҮР ҮГ БА МЭРГЭЖЛИЙН НЭР ТОМЬЁО (Keywords & Glossary)
| # | Англи нэр томьёо | Монгол утга | Дэлгэрэнгүй тайлбар |
|---|---|---|---|
| 1 | Design Pattern | Дизайн паттерн / Загвар хэв | Програм хангамжийн дизайнд байнга давтагддаг асуудлыг шийдвэрлэх батлагдсан загвар. Хоолны жортой адил — нэг удаа сурч, олон удаа ашиглана. |
| 2 | Gang of Four (GoF) | Дөрвөн хүний бүлэг | 1995 онд "Design Patterns" номыг бичсэн 4 зохиогч: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. |
| 3 | Creational Pattern | Бүтээх паттерн | Объект ЯАЖЕ үүсгэхтэй холбоотой паттерн. Объект үүсгэх процессыг хийсвэрлэж, системийг уян хатан болгодог. |
| 4 | Structural Pattern | Бүтцийн паттерн | Класс, объектуудыг ЯАЖЕ нэгтгэхтэй холбоотой. Том бүтэц зохион байгуулах арга замыг тодорхойлдог. |
| 5 | Behavioral Pattern | Зан төлөвийн паттерн | Объектууд ЯАЖЕ харилцахтай холбоотой. Алгоритм, хариуцлагыг хуваарилах аргуудыг тодорхойлдог. |
| 6 | Singleton | Ганцаардмал | Зөвхөн НЭГ объект үүсгэж, глобал хандалт хангах паттерн. Ерөнхийлөгч ганцхан байдаг шиг. |
| 7 | Factory Method | Үйлдвэрийн арга | Объект үүсгэх үүргийг дэд класст шилжүүлдэг. Пиццаны дэлгүүр шиг — захиалга өгөхөд бэлэн бүтээгдэхүүн гарна. |
| 8 | Abstract Factory | Хийсвэр үйлдвэр | Хамааралтай объектуудын ГЭР БҮЛИЙГ нэг дор үүсгэдэг. Тавилга дэлгүүрийн каталог шиг — "Модерн" сонговол бүх тавилга модерн. |
| 9 | Builder | Бүтээгч | Нарийн төвөгтэй объектыг алхам алхмаар бүтээх. Бургер захиалах шиг — орц бүрийг тусдаа сонгоно. |
| 10 | Prototype | Эх загвар / Хуулбарлагч | Одоо байгаа объектыг хуулбарлаж шинэ объект үүсгэх. Маягтыг хуулбарлаад зөвхөн өөрчлөх зүйлээ засах шиг. |
| 11 | Adapter | Тохируулагч / Залгуур хувиргагч | Нийцдэггүй хоёр интерфейсийг холбодог. Гадаадын залгуурын хувиргагч шиг. |
| 12 | Facade | Нүүр хаалга | Нарийн системд хялбар, ганцхан интерфейс хангадаг. Зочид буудлын ресепшн шиг — бүх зүйлийг нэг цэгээс шийднэ. |
| 13 | Decorator | Чимэглэгч | Объектод шинэ функц динамикаар давхарлаж нэмдэг. Кофенд сүү, чихэр нэмдэг шиг. |
| 14 | Proxy | Орлогч / Төлөөлөгч | Өөр объектын орлуулагч бөгөөд хандалтыг хянаж, нэмэлт логик нэмдэг. Нарийн бичгийн дарга шиг. |
| 15 | Observer | Ажиглагч | Объектын төлөв өөрчлөгдөхөд бүх дагагчдад мэдэгдэдэг. YouTube subscribe шиг. |
| 16 | Strategy | Стратеги | Олон алгоритмаас ажиллах үед сонгож болдог. Ажил руугаа явах олон арга шиг — автобус, такси, алхах. |
| 17 | Template Method | Загвар арга | Алгоритмын бүтцийг тогтоож, нарийн алхмуудыг дэд класст шилжүүлдэг. Бууз, хуушууры ерөнхий "хоол хийх" дараалал ижил, нарийн нь өөр. |
| 18 | Command | Команд | Хүсэлтийг объект болгож хадгалах, дараалалд оруулах, буцаах (undo). Ресторанд захиалгын цаас шиг. |
| 19 | State | Төлөв | Объект дотоод төлөвөөс хамаарч зан төлөвөө өөрчилдөг. Гар утас: дуугүй горимд дуугардаггүй, чимээтэй горимд дуугардаг. |
| 20 | Iterator | Давталтчин | Цуглуулгын элементүүдийг дотоод бүтцийг далдалж дараалуулан хандах. TV-ийн суваг солигч шиг — дарах бүрт дараагийн суваг. |
| 21 | Mediator | Зуучлагч | Объектуудын шууд харилцааг зогсоож, бүхнийг нэг зуучлагчаар дамжуулдаг. Нисэх буудлын удирдах цамхаг шиг. |
| 22 | Memento | Дурсгал / Хадгалагч | Объектын өмнөх төлөвийг хадгалж, хэрэгтэй үед сэргээдэг. Тоглоомын "save point" шиг. |
| 23 | Chain of Responsibility | Хариуцлагын гинж | Хүсэлтийг гинжин хэлхээгээр дамжуулж, тохирох зохицуулагч олтол дамжуулдаг. Байгууллагад өргөдөл дамжуулах шиг. |
| 24 | Visitor | Зочин | Объектын бүтцийг өөрчлөхгүйгээр шинэ үйлдэл нэмэх. Гэрийн эзэн зочинд гэрээ нээж өгдөг шиг — зочин шинэ үйлдэл хийнэ. |
| 25 | Bridge | Гүүр | Хийсвэрлэлийг хэрэгжүүлэлтээс салгаж, тус тусдаа хөгжүүлэх. Удирдлагын пульт (хийсвэрлэл) ба ТВ (хэрэгжүүлэлт) тусдаа. |
| 26 | Composite | Нийлмэл | Объектуудыг мод (tree) бүтцэд зохион байгуулж, нэг объект шиг хандах. Файлын систем: фолдер дотор файл ба фолдер байна. |
| 27 | Flyweight | Хөнгөн жин | Олон ижил объектын нийтлэг өгөгдлийг хуваалцуулж санах ойг хэмнэдэг. Тоглоомд олон мод ижил бүтэцтэй — зүгээр байрлал нь өөр. |
| 28 | Interpreter | Тайлбарлагч | Хэлний дүрэм тодорхойлж, өгүүлбэрийг тайлбарлах. SQL query-г задлан шинжлэх шиг. |
| 29 | SOLID | SOLID зарчмууд | Объект хандлагат дизайны 5 суурь зарчим: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion. |
| 30 | Open/Closed Principle | Нээлттэй/Хаалттай зарчим | Класс ӨРГӨТГӨХӨД нээлттэй, ӨӨРЧЛӨХӨД хаалттай байх ёстой. Decorator, Strategy паттернууд үүнийг хэрэгжүүлдэг. |
| 31 | Dependency Inversion | Хамааралын урвуу | Дээд түвшний модуль доод түвшний модулиас хамаарах ёсгүй — хоёулаа хийсвэрлэлээс хамаарах ёстой. |
| 32 | Loose Coupling | Сул холбоос | Модулиуд хоорондоо аль болох бага хамааралтай байх. Паттернуудын ихэнх нь үүнийг зорьдог. |
| 33 | Interface | Интерфейс | Класс ямар үйлдлүүд хийж чадахыг тодорхойлсон гэрээ. Хэрэгжүүлэлтийг заадаггүй, зөвхөн "юу хийх"-ийг заадаг. |
| 34 | Abstract Class | Хийсвэр класс | Дутуу хэрэгжүүлэлттэй (abstract method-тэй) класс. Шууд объект үүсгэж болохгүй, зөвхөн удамшуулна. |
| 35 | Inheritance | Удамшил | Нэг классаас шинж чанар, үйлдлүүдийг өвлөн авах. Хүүхэд эцэг эхийнхээ зарим шинж чанарыг өвлөдөг шиг. |
| 36 | Composition | Бүтэц / Нийлбэр | Объектыг бусад объектуудаас бүрдүүлэх. "HAS-A" харилцаа. Машин = мотор + дугуй + бие. |
| 37 | Encapsulation | Капсулжуулалт | Өгөгдөл ба методыг нэгтгэж, гаднаас шууд хандахыг хязгаарлах. |
| 38 | Polymorphism | Олон хэлбэрт байдал | Нэг интерфейсээр өөр өөр хэрэгжүүлэлтийг дуудах чадвар. Strategy, Observer паттернуудын суурь. |
| 39 | Over-engineering | Хэт инженерчлэл | Шаардлагагүй нарийн төвөгтэй шийдэл хэрэглэх. Паттерн хэрэггүй газар паттерн хэрэглэх жишээ. |
| 40 | Design for Change | Өөрчлөлтөд зориулсан дизайн | GoF-ийн гол зарчим — систем дизайнлахдаа зайлшгүй ирэх өөрчлөлтийг урьдчилан тооцох. |