3.1 Лабораторийн зорилго
Энэ лабораторид та 4 үндсэн дизайн паттерныг Java дээр бие даан хэрэгжүүлнэ:
- Singleton
- Factory Method
- Observer
- Strategy
Шаардлагатай зүйлс: Лекц 01-д суулгасан Java JDK 17+ ба Eclipse IDE
3.2 Лаб 1: Singleton паттерн — Тохиргооны менежер
Даалгавар: Програмын тохиргоог удирдах ConfigManager класс бичих. Зөвхөн нэг объект байх ёстой.
Алхам 1: Eclipse-д шинэ төсөл үүсгэнэ (File → New → Java Project): DesignPatternsLab
Алхам 2: ConfigManager.java файл үүсгэнэ:
import java.util.HashMap;
import java.util.Map;
public class ConfigManager {
// === Singleton хэрэгжүүлэлт ===
// 1. Ганцхан instance хадгалах static талбар
private static ConfigManager instance;
// 2. Тохиргооны утгуудыг хадгалах map
private Map<String, String> settings;
// 3. Private constructor — гаднаас new хийж чадахгүй
private ConfigManager() {
settings = new HashMap<>();
// Анхдагч тохиргоо
settings.put("language", "mn"); // Хэл: Монгол
settings.put("theme", "dark"); // Загвар: Харанхуй
settings.put("fontSize", "14"); // Фонт хэмжээ: 14
System.out.println("✅ ConfigManager үүслээ!");
}
// 4. Ганцхан instance-д хандах арга
public static ConfigManager getInstance() {
if (instance == null) { // Анх удаа дуудахад л үүсгэнэ
instance = new ConfigManager();
}
return instance;
}
// 5. Тохиргоо авах
public String get(String key) {
return settings.getOrDefault(key, "тодорхойгүй");
}
// 6. Тохиргоо тохируулах
public void set(String key, String value) {
settings.put(key, value);
System.out.println("⚙️ Тохиргоо өөрчлөгдлөө: " + key + " = " + value);
}
// 7. Бүх тохиргоог харуулах
public void showAll() {
System.out.println("\n📋 Бүх тохиргоо:");
for (Map.Entry<String, String> entry : settings.entrySet()) {
System.out.println(" " + entry.getKey() + " = " + entry.getValue());
}
}
}
Алхам 3: SingletonTest.java файлд тестлэх:
public class SingletonTest {
public static void main(String[] args) {
// Анхны instance авах
ConfigManager config1 = ConfigManager.getInstance();
config1.showAll();
// Тохиргоо өөрчлөх
config1.set("theme", "light");
config1.set("fontSize", "16");
// Хоёр дахь instance авах — ижил объект байх ёстой!
ConfigManager config2 = ConfigManager.getInstance();
config2.showAll();
// Шалгах: хоёулаа ижил объект уу?
System.out.println("\nconfig1 == config2: " + (config1 == config2));
// → true (яг нэг объект!)
// ConfigManager cm = new ConfigManager(); // ← АЛДАА! private constructor
}
}
Хүлээгдэх үр дүн:
✅ ConfigManager үүслээ!
📋 Бүх тохиргоо:
language = mn
theme = dark
fontSize = 14
⚙️ Тохиргоо өөрчлөгдлөө: theme = light
⚙️ Тохиргоо өөрчлөгдлөө: fontSize = 16
📋 Бүх тохиргоо:
language = mn
theme = light
fontSize = 16
config1 == config2: true
3.3 Лаб 2: Factory Method паттерн — Дүрс үүсгэгч
Даалгавар: Өөр өөр дүрс (Shape) үүсгэдэг Factory бичих.
// ===== Shape интерфейс =====
interface Shape {
void draw(); // Дүрс зурах
double getArea(); // Талбай тооцоолох
}
// ===== Бодит дүрсүүд =====
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public void draw() {
System.out.println("⭕ Тойрог зурлаа (радиус: " + radius + ")");
}
@Override
public double getArea() {
return Math.PI * radius * radius; // S = πr²
}
}
class Rectangle implements Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("⬜ Тэгш өнцөгт зурлаа (" + width + " x " + height + ")");
}
@Override
public double getArea() {
return width * height; // S = w × h
}
}
class Triangle implements Shape {
private double base, height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
public void draw() {
System.out.println("🔺 Гурвалжин зурлаа (суурь: " + base + ", өндөр: " + height + ")");
}
@Override
public double getArea() {
return 0.5 * base * height; // S = ½ × b × h
}
}
// ===== Factory =====
class ShapeFactory {
public static Shape create(String type, double... params) {
switch (type.toLowerCase()) {
case "circle":
return new Circle(params[0]);
case "rectangle":
return new Rectangle(params[0], params[1]);
case "triangle":
return new Triangle(params[0], params[1]);
default:
throw new IllegalArgumentException("Тодорхойгүй дүрс: " + type);
}
}
}
// ===== Тест =====
class FactoryTest {
public static void main(String[] args) {
// Factory-аар дүрсүүд үүсгэх
Shape circle = ShapeFactory.create("circle", 5);
Shape rect = ShapeFactory.create("rectangle", 4, 6);
Shape tri = ShapeFactory.create("triangle", 3, 8);
// Бүгдийг зурж, талбайг тооцоолох
Shape[] shapes = {circle, rect, tri};
for (Shape s : shapes) {
s.draw();
System.out.println(" Талбай: " + String.format("%.2f", s.getArea()));
}
}
}
3.4 Лаб 3: Observer паттерн — Цаг агаарын мэдээ
Даалгавар: Цаг агаарын станц өгөгдөл өөрчлөгдөхөд бүх дэлгэцүүдэд мэдэгдэх систем бичих.
import java.util.ArrayList;
import java.util.List;
// ===== Observer интерфейс =====
interface WeatherDisplay {
void update(double temperature, double humidity);
}
// ===== Subject — Цаг агаарын станц =====
class WeatherStation {
private List<WeatherDisplay> displays = new ArrayList<>();
private double temperature;
private double humidity;
// Дэлгэц бүртгэх
public void addDisplay(WeatherDisplay display) {
displays.add(display);
}
// Дэлгэц хасах
public void removeDisplay(WeatherDisplay display) {
displays.remove(display);
}
// Өгөгдөл шинэчлэгдэхэд бүх дэлгэцэд мэдэгдэх
public void setMeasurements(double temperature, double humidity) {
this.temperature = temperature;
this.humidity = humidity;
System.out.println("\n🌡️ Шинэ хэмжилт: " + temperature + "°C, " + humidity + "% чийгшил");
notifyDisplays();
}
private void notifyDisplays() {
for (WeatherDisplay display : displays) {
display.update(temperature, humidity);
}
}
}
// ===== Бодит дэлгэцүүд =====
class PhoneDisplay implements WeatherDisplay {
private String ownerName;
public PhoneDisplay(String ownerName) { this.ownerName = ownerName; }
@Override
public void update(double temperature, double humidity) {
System.out.println("📱 " + ownerName + "-ийн утас: " + temperature + "°C, " + humidity + "%");
}
}
class TVDisplay implements WeatherDisplay {
private String channel;
public TVDisplay(String channel) { this.channel = channel; }
@Override
public void update(double temperature, double humidity) {
String feeling = temperature < 0 ? "❄️ Хүйтэн" : temperature < 20 ? "🌤️ Сэрүүн" : "☀️ Дулаан";
System.out.println("📺 " + channel + ": " + feeling + " " + temperature + "°C");
}
}
// ===== Тест =====
class ObserverTest {
public static void main(String[] args) {
// Станц үүсгэх
WeatherStation station = new WeatherStation();
// Дэлгэцүүд бүртгэх
PhoneDisplay bat = new PhoneDisplay("Бат");
PhoneDisplay bold = new PhoneDisplay("Болд");
TVDisplay mnb = new TVDisplay("MNB");
station.addDisplay(bat);
station.addDisplay(bold);
station.addDisplay(mnb);
// Хэмжилт шинэчлэх → Бүгдэд мэдэгдэнэ
station.setMeasurements(25.5, 60);
station.setMeasurements(-15.0, 80);
// Болдын дэлгэцийг хасах
station.removeDisplay(bold);
station.setMeasurements(10.0, 45);
}
}
3.5 Лаб 4: Strategy паттерн — Хөнгөлөлтийн систем
Даалгавар: Дэлгүүрийн хөнгөлөлтийн олон стратеги (хувиар, тогтмол дүнгээр, шинэ жилийн) хэрэгжүүлэх.
// ===== Strategy интерфейс =====
interface DiscountStrategy {
double applyDiscount(double originalPrice);
String getDescription();
}
// ===== Бодит стратегиуд =====
class PercentageDiscount implements DiscountStrategy {
private double percent;
public PercentageDiscount(double percent) { this.percent = percent; }
@Override
public double applyDiscount(double originalPrice) {
return originalPrice * (1 - percent / 100);
}
@Override
public String getDescription() {
return percent + "% хөнгөлөлт";
}
}
class FixedDiscount implements DiscountStrategy {
private double amount;
public FixedDiscount(double amount) { this.amount = amount; }
@Override
public double applyDiscount(double originalPrice) {
return Math.max(0, originalPrice - amount); // 0-ээс бага болохгүй
}
@Override
public String getDescription() {
return amount + "₮ хөнгөлөлт";
}
}
class NewYearDiscount implements DiscountStrategy {
@Override
public double applyDiscount(double originalPrice) {
if (originalPrice > 100000) {
return originalPrice * 0.7; // 100,000₮-аас дээш → 30% хөнгөлөлт
}
return originalPrice * 0.9; // Бусад → 10% хөнгөлөлт
}
@Override
public String getDescription() {
return "🎄 Шинэ жилийн хөнгөлөлт (100K+ → 30%, бусад → 10%)";
}
}
// ===== Context — Дэлгүүр =====
class Shop {
private DiscountStrategy discountStrategy;
public void setDiscountStrategy(DiscountStrategy strategy) {
this.discountStrategy = strategy;
System.out.println("🏷️ Стратеги: " + strategy.getDescription());
}
public void checkout(String item, double price) {
double finalPrice = discountStrategy.applyDiscount(price);
System.out.println(" " + item + ": " + price + "₮ → "
+ String.format("%.0f", finalPrice) + "₮"
+ " (хэмнэлт: " + String.format("%.0f", price - finalPrice) + "₮)");
}
}
// ===== Тест =====
class StrategyTest {
public static void main(String[] args) {
Shop shop = new Shop();
// Стратеги 1: 20% хөнгөлөлт
shop.setDiscountStrategy(new PercentageDiscount(20));
shop.checkout("Өвлийн курт", 150000);
// Стратеги 2: 5000₮ хөнгөлөлт
shop.setDiscountStrategy(new FixedDiscount(5000));
shop.checkout("Номхон", 25000);
// Стратеги 3: Шинэ жилийн хөнгөлөлт
shop.setDiscountStrategy(new NewYearDiscount());
shop.checkout("Зурагт", 500000);
shop.checkout("Чихэвч", 30000);
}
}