ЛЕКЦ 04: КОДЫН ДАХИН ЗАСВАРЛАЛТ (Refactoring)
Хичээлийн зорилго: Кодын дахин засварлалт (refactoring)-ийн суурь ойлголт, code smell таних, нийтлэг refactoring техникүүд, Java дээр бодит жишээнүүдтэйгээр эзэмшүүлэх.
Хамрах хүрээ: Refactoring тодорхойлолт, Code Smell-ийн төрлүүд, нийтлэг refactoring техникүүд, Martin Fowler-ийн каталог, SOLID-тэй холбоос, IDE refactoring хэрэгслүүд.
ХЭСЭГ 1: ОНОЛЫН СУУРЬ (Theory & Foundations)
1.1 Refactoring гэж юу вэ?
Refactoring гэдэг нь кодын гадаад зан төлөвийг өөрчлөхгүйгээр дотоод бүтцийг сайжруулах процесс.
🔑 Martin Fowler-ийн тодорхойлолт: "Refactoring нь програм хангамжийн дотоод бүтцийг өөрчлөх арга бөгөөд гадаад зан төлөвийг хадгалсан нөхцөлд кодыг илүү ойлгомжтой, засвар хялбар болгоно."
Зүйрлэл:
💡 Байшинг гаднаас нь харахад ИЖИЛ харагддаг, гэхдээ дотоод цахилгааны утас, сантехникийг шинэчилсэн шиг — refactoring нь кодын "дотоод засвар".
Refactoring юу БИШ вэ?
| Refactoring | Refactoring БИШ |
|---|---|
| Кодын бүтцийг сайжруулах | Шинэ функц нэмэх |
| Уншихад хялбар болгох | Алдаа засах (bug fix) |
| Засвар хялбар болгох | Гүйцэтгэлийг сайжруулах (optimization) |
| Давхардлыг арилгах | Бүтэн дахин бичих (rewrite) |
⚠️ Чухал: Refactoring хийхдээ гадаад зан төлөв ӨӨРЧЛӨГДӨХГҮЙ. Өөрөөр хэлбэл, refactoring-ийн өмнө ба дараа тестүүд ИЖИЛ ҮР ДҮНТЭЙ дамжих ёстой.
1.2 Refactoring яагаад чухал вэ?
Кодын "ялзрал" (Code Decay):
Цаг хугацаа өнгөрөхтүтэй код ялзардаг — шинэ функц нэмэх, алдаа засах, хөгжүүлэгчид солигдох бүрд кодын бүтэц муудаж эхэлдэг.
💡 Зүйрлэл: Гэрийн цэвэрлэгээ хийхгүй бол бохир овоорч, юм олоход хэцүү болдог. Код ч мөн адил — байнгын "цэвэрлэгээ" хэрэгтэй.
Refactoring-ийн давуу талууд:
| # | Давуу тал | Тайлбар |
|---|---|---|
| 1 | Код ойлгомжтой болно | Шинэ хөгжүүлэгч хурдан ойлгоно |
| 2 | Алдаа олоход хялбар | Цэвэр кодонд алдаа нуугдах боломж бага |
| 3 | Шинэ функц нэмэхэд хурдан | Цэвэр бүтцэнд шинэ зүйл оруулахад хялбар |
| 4 | Засвар хялбар | Нэг газар өөрчилж, олон газар нөлөөлөхгүй |
| 5 | Багийн бүтээмж нэмэгдэнэ | Хүн бүр ойлгож, хамтран ажиллаж чадна |
| 6 | Техникийн өрийг бууруулна | Ирээдүйд засвар хийх зардал буурна |
Техникийн өр (Technical Debt):
💡 Зүйрлэл: Банкны зээл шиг — одоо хурдан, бохир код бичвэл (зээл авбал), ирээдүйд ХҮҮТЭЙГЭЭР буцааж засах хэрэгтэй (өр төлөх). Refactoring нь тэр "өрийг" буцааж төлөх процесс.
Техникийн өрийн хуримтлал:
│
│ ╱ Өр ихсэх (refactoring хийхгүй)
Засвар хийх │ ╱
зардал │ ╱
│ ╱
│ ╱
│╱───── Refactoring хийх → Зардал тогтвортой
│
└──────────────────── Цаг хугацаа →
1.3 Code Smell гэж юу вэ?
Code Smell ("Кодын үнэр") гэдэг нь кодонд асуудал байж болзошгүйг илтгэдэг шинж тэмдэг. Алдаа биш, гэхдээ кодын чанар МУУДАЖ байгаагийн дохио.
💡 Зүйрлэл: Хоолны хадгалах хугацаа дуусаагүй ч МУУХАЙ ҮНЭРТЭЖ байвал — асуудалтай байж магадгүй. Code Smell нь кодын "муухай үнэр".
1.3.1 Bloaters (Хэт томорсон код)
Код, метод, класс хэт том болсон, удирдахад хэцүү.
а) Long Method (Урт метод)
// ❌ БУРУУ — 80+ мөр, олон зүйл хийж байна
public void processOrder(Order order) {
// Хэрэглэгч шалгах (20 мөр)
if (order.getCustomer() == null) { ... }
if (!order.getCustomer().isActive()) { ... }
// Бүтээгдэхүүн нөөц шалгах (15 мөр)
for (Item item : order.getItems()) { ... }
// Үнэ тооцоолох (20 мөр)
double total = 0;
for (Item item : order.getItems()) { total += ...; }
// Хөнгөлөлт тооцоолох (15 мөр)
if (order.getCustomer().isVIP()) { ... }
// Төлбөр боловсруулах (10 мөр)
paymentService.charge(order.getCustomer(), total);
// Баримт илгээх (10 мөр)
emailService.sendReceipt(order.getCustomer(), total);
}
// ✅ ЗӨӨВ — Жижиг методуудад хуваасан (Extract Method)
public void processOrder(Order order) {
validateCustomer(order.getCustomer());
checkInventory(order.getItems());
double total = calculateTotal(order);
double discountedTotal = applyDiscount(order.getCustomer(), total);
processPayment(order.getCustomer(), discountedTotal);
sendReceipt(order.getCustomer(), discountedTotal);
}
private void validateCustomer(Customer customer) { ... }
private void checkInventory(List<Item> items) { ... }
private double calculateTotal(Order order) { ... }
private double applyDiscount(Customer customer, double total) { ... }
private void processPayment(Customer customer, double amount) { ... }
private void sendReceipt(Customer customer, double amount) { ... }
б) Large Class (Том класс)
Нэг класс хэт олон үүрэг (responsibility) хариуцаж байна → Single Responsibility Principle зөрчиж байна.
// ❌ БУРУУ — UserManager бүх зүйл хийж байна
class UserManager {
public void createUser() { ... }
public void deleteUser() { ... }
public void sendEmail() { ... } // Имэйл илгээх → өөр үүрэг
public void generateReport() { ... } // Тайлан гаргах → өөр үүрэг
public void backupDatabase() { ... } // Нөөцлөлт → өөр үүрэг
}
// ✅ ЗӨӨВ — Үүргийг тусгаарласан (Extract Class)
class UserService {
public void createUser() { ... }
public void deleteUser() { ... }
}
class EmailService {
public void sendEmail() { ... }
}
class ReportService {
public void generateReport() { ... }
}
class BackupService {
public void backupDatabase() { ... }
}
в) Primitive Obsession (Анхдагч төрлийн хэт хэрэглээ)
String, int зэрэг энгийн төрлийг хэт их ашиглаж, утга бүхий объект үүсгэхгүй байх.
// ❌ БУРУУ — Утасны дугаар, имэйл гэх мэт String-ээр
public void createUser(String name, String phone, String email,
String street, String city, String zipCode) { ... }
// ✅ ЗӨӨВ — Утга бүхий объект (Value Object)
public void createUser(String name, PhoneNumber phone, Email email,
Address address) { ... }
class PhoneNumber {
private String value;
public PhoneNumber(String value) {
if (!value.matches("\\d{8}")) throw new IllegalArgumentException("Буруу дугаар");
this.value = value;
}
}
class Address {
private String street;
private String city;
private String zipCode;
}
г) Long Parameter List (Олон параметр)
Метод хэт олон параметр авч байна.
// ❌ БУРУУ — 7 параметр
public void createEvent(String name, String desc, Date start, Date end,
String location, int maxPeople, double price) { ... }
// ✅ ЗӨӨВ — Parameter Object ашиглах
public void createEvent(EventConfig config) { ... }
class EventConfig {
private String name;
private String description;
private Date startDate;
private Date endDate;
private String location;
private int maxCapacity;
private double price;
// Builder pattern ашиглаж болно
}
1.3.2 Object-Orientation Abusers (ООП-ийн буруу хэрэглээ)
а) Switch Statements (Switch давтамж)
Ижил switch/if-else олон газар давтагдаж байна.
// ❌ БУРУУ — switch олон газар давтагдана
public double calculatePay(Employee emp) {
switch (emp.getType()) {
case ENGINEER: return emp.getSalary();
case MANAGER: return emp.getSalary() + emp.getBonus();
case INTERN: return emp.getSalary() * 0.5;
}
return 0;
}
public String getTitle(Employee emp) {
switch (emp.getType()) {
case ENGINEER: return "Инженер";
case MANAGER: return "Менежер";
case INTERN: return "Дадлагажигч";
}
return "";
}
// ✅ ЗӨӨВ — Polymorphism ашиглах (Replace Conditional with Polymorphism)
abstract class Employee {
abstract double calculatePay();
abstract String getTitle();
}
class Engineer extends Employee {
double calculatePay() { return getSalary(); }
String getTitle() { return "Инженер"; }
}
class Manager extends Employee {
double calculatePay() { return getSalary() + getBonus(); }
String getTitle() { return "Менежер"; }
}
б) Refused Bequest (Татгалзсан өв)
Дэд класс нь эх классын ихэнх метод/шинж чанарыг ашигладаггүй.
💡 Зүйрлэл: Аав нь тариачин, хүү нь программист — аавынхаа бүх тариалангийн багаж хэрэгслийг "өвлөж" авсан ч ашигладаггүй.
1.3.3 Change Preventers (Өөрчлөлтөд саад)
Нэг газар өөрчлөлт хийхэд олон газарт нөлөөлдөг.
а) Divergent Change (Салангид өөрчлөлт)
Нэг класст олон өөр шалтгаанаар өөрчлөлт хийх шаардлагатай.
б) Shotgun Surgery (Сум шиг тархалт)
Нэг өөрчлөлт хийхэд олон класст засвар хийх шаардлагатай.
💡 Зүйрлэл: Цахилгааны утас солиход бүх өрөөнд нэвтрэх шаардлагатай бол — бүтэц муу байна.
1.3.4 Dispensables (Шаардлагагүй зүйлс)
а) Dead Code (Үхсэн код)
Хэзээ ч ажиллахгүй, дуудагдахгүй код.
// ❌ БУРУУ — Хэзээ ч дуудагдахгүй метод
public void oldMethod() {
// Энэ метод хаанаас ч дуудагдахгүй
}
// if (false) { ... } — Хэзээ ч ажиллахгүй
б) Duplicate Code (Давхардсан код)
Ижил эсвэл маш төстэй код олон газар давтагдаж байна.
// ❌ БУРУУ — Ижил код 2 газар
public double calculateStudentDiscount(double price) {
double tax = price * 0.1;
double discount = price * 0.2;
return price + tax - discount;
}
public double calculateTeacherDiscount(double price) {
double tax = price * 0.1;
double discount = price * 0.15;
return price + tax - discount;
}
// ✅ ЗӨӨВ — Нийтлэг метод гаргах (Extract Method)
public double calculatePrice(double price, double discountRate) {
double tax = price * 0.1;
double discount = price * discountRate;
return price + tax - discount;
}
public double calculateStudentDiscount(double price) {
return calculatePrice(price, 0.2);
}
public double calculateTeacherDiscount(double price) {
return calculatePrice(price, 0.15);
}
в) Speculative Generality (Шаардлагагүй ерөнхийлөлт)
Ирээдүйд хэрэгтэй БАЙЖ МАГАДГҮЙ гэж бодож шаардлагагүй абстракци үүсгэсэн.
💡 Зүйрлэл: "Ирээдүйд хэрэг болно" гэж 50 ширхэг цүнх авсан ч хэзээ ч ашиглаагүй.
г) Comments (Хэт олон тайлбар)
Код ОЙЛГОМЖТОЙ биш учраас тайлбараар нөхсөн.
// ❌ БУРУУ — Код муу, тайлбараар нөхсөн
// Хэрэглэгчийн насыг шалгах, 18-аас дээш бол true буцаах
// Хэрэв нас нь null бол false буцаах
public boolean chk(Integer a) {
if (a == null) return false;
return a >= 18;
}
// ✅ ЗӨӨВ — Код өөрөө тайлбарлагдаж байна
public boolean isAdult(Integer age) {
if (age == null) return false;
return age >= 18;
}
1.3.5 Couplers (Хэт нягт холбоос)
а) Feature Envy (Бусдын шинж чанарт шунах)
Метод нь өөрийн классын өгөгдлөөс илүү ӨӨӨР классын өгөгдлийг ашигладаг.
// ❌ БУРУУ — OrderPrinter нь Order-ийн бүх зүйлийг мэдэж байна
class OrderPrinter {
public String format(Order order) {
return order.getCustomerName() + ": " +
order.getItems().size() + " бүтээгдэхүүн, " +
order.getTotal() + "₮, " +
order.getDiscount() + "₮ хөнгөлөлт";
}
}
// ✅ ЗӨӨВ — Order класст format метод нэмэх (Move Method)
class Order {
public String format() {
return customerName + ": " + items.size() + " бүтээгдэхүүн, " +
total + "₮, " + discount + "₮ хөнгөлөлт";
}
}
б) Message Chains (Мессежийн гинж)
// ❌ БУРУУ — Гинж маягаар олон объектоор дамжих
String city = order.getCustomer().getAddress().getCity().getName();
// ✅ ЗӨӨВ — Delegate метод (Hide Delegate)
String city = order.getCustomerCity(); // Order дотор getCustomerCity() метод
1.4 Нийтлэг Refactoring Техникүүд
1.4.1 Extract Method (Метод гаргах)
Хэзээ: Урт метод, давхардсан код, тайлбар бичих шаардлагатай код блок.
// ӨМНӨ:
public void printReport(List<Student> students) {
// Толгой хэвлэх
System.out.println("=== Оюутны тайлан ===");
System.out.println("Огноо: " + LocalDate.now());
System.out.println("---");
// Оюутнуудын мэдээлэл хэвлэх
for (Student s : students) {
System.out.println(s.getName() + " - " + s.getGPA());
}
// Хөл хэвлэх
System.out.println("---");
System.out.println("Нийт: " + students.size() + " оюутан");
}
// ДАРАА:
public void printReport(List<Student> students) {
printHeader();
printStudentDetails(students);
printFooter(students.size());
}
private void printHeader() {
System.out.println("=== Оюутны тайлан ===");
System.out.println("Огноо: " + LocalDate.now());
System.out.println("---");
}
private void printStudentDetails(List<Student> students) {
for (Student s : students) {
System.out.println(s.getName() + " - " + s.getGPA());
}
}
private void printFooter(int count) {
System.out.println("---");
System.out.println("Нийт: " + count + " оюутан");
}
1.4.2 Rename (Нэр өөрчлөх)
Хэзээ: Хувьсагч, метод, классын нэр утга учиргүй.
// ӨМНӨ:
int d; // Юу гэсэн утга вэ?
public void proc(List<String> l) { ... }
class Mgr { ... }
// ДАРАА:
int daysSinceCreation; // Тодорхой!
public void processNames(List<String> names) { ... }
class UserManager { ... }
1.4.3 Replace Magic Number with Named Constant (Шидэт тоог нэрлэсэн тогтмолоор солих)
// ӨМНӨ:
if (age >= 18) { ... } // 18 юу гэсэн утга вэ?
double tax = price * 0.1; // 0.1 юу вэ?
if (students.size() > 30) { ... } // 30 яагаад?
// ДАРАА:
private static final int LEGAL_AGE = 18;
private static final double TAX_RATE = 0.1;
private static final int MAX_CLASS_SIZE = 30;
if (age >= LEGAL_AGE) { ... }
double tax = price * TAX_RATE;
if (students.size() > MAX_CLASS_SIZE) { ... }
1.4.4 Extract Class (Класс гаргах)
Хэзээ: Нэг класс хэт олон үүрэг хариуцаж байна.
// ӨМНӨ:
class Student {
String name;
String street;
String city;
String zipCode;
String phone;
String email;
}
// ДАРАА:
class Student {
String name;
Address address;
ContactInfo contact;
}
class Address {
String street;
String city;
String zipCode;
}
class ContactInfo {
String phone;
String email;
}
1.4.5 Replace Conditional with Polymorphism (Нөхцөлийг полиморфизмаар солих)
// ӨМНӨ:
class Shape {
String type;
double area() {
if (type.equals("circle")) {
return Math.PI * radius * radius;
} else if (type.equals("rectangle")) {
return width * height;
} else if (type.equals("triangle")) {
return 0.5 * base * height;
}
return 0;
}
}
// ДАРАА:
interface Shape {
double area();
}
class Circle implements Shape {
private double radius;
public double area() { return Math.PI * radius * radius; }
}
class Rectangle implements Shape {
private double width, height;
public double area() { return width * height; }
}
class Triangle implements Shape {
private double base, height;
public double area() { return 0.5 * base * height; }
}
1.4.6 Introduce Parameter Object (Параметрийн объект оруулах)
// ӨМНӨ:
public List<Event> findEvents(Date startDate, Date endDate,
String location, int minCapacity) { ... }
// ДАРАА:
public List<Event> findEvents(EventSearchCriteria criteria) { ... }
class EventSearchCriteria {
private Date startDate;
private Date endDate;
private String location;
private int minCapacity;
}
1.4.7 Move Method / Move Field (Метод/Талбар зөөх)
// ӨМНӨ: Order дахь метод Customer-ийн өгөгдлийг илүү ашиглаж байна
class Order {
double getDiscountedPrice() {
if (customer.getMemberYears() > 5 && customer.isPremium()) {
return price * 0.8;
}
return price;
}
}
// ДАРАА: Метод Customer руу зөөгдсөн
class Customer {
double getDiscountRate() {
if (memberYears > 5 && isPremium) return 0.8;
return 1.0;
}
}
class Order {
double getDiscountedPrice() {
return price * customer.getDiscountRate();
}
}
1.4.8 Replace Temp with Query (Түр хувьсагчийг query-гээр солих)
// ӨМНӨ:
double basePrice = quantity * itemPrice;
if (basePrice > 1000) {
return basePrice * 0.95;
}
// ДАРАА:
if (getBasePrice() > 1000) {
return getBasePrice() * 0.95;
}
private double getBasePrice() {
return quantity * itemPrice;
}
1.5 Refactoring хэзээ хийх вэ?
"Гурвын дүрэм" (Rule of Three):
"Ижил зүйлийг 3 дахь удаа давтахад refactor хий." — Don Roberts
- Анх удаа — Зүгээр хий
- 2 дахь удаа — Давтагдаж байна гэдгийг анзаар, гэхдээ зүгээр хий
- 3 дахь удаа — Refactor хий!
Refactoring хийх цагууд:
| Цаг | Тайлбар |
|---|---|
| Шинэ функц нэмэхийн өмнө | Шинэ зүйл нэмэхэд хялбар болгох |
| Алдаа засахад | Алдааг олоход хэцүү байсан бол — код ойлгомжтой биш |
| Код шалгалтын үед (Code Review) | Баг бусад хөгжүүлэгчийн кодыг шалгахад |
| Техникийн өр хуримтлагдсан үед | Sprint backlog-д refactoring task нэмэх |
Refactoring-ийн алхамууд:
1. 🧪 Тест бэлтгэх — Одоо байгаа зан төлөвийг тестлэх
2. 📝 Code Smell олох — Ямар асуудал байна?
3. 🔧 Жижиг алхмаар засах — Нэг удаад нэг refactoring
4. 🧪 Тест ажиллуулах — Зан төлөв өөрчлөгдөөгүй гэдгийг шалгах
5. 🔄 Давтах — Дараагийн smell руу шилжих
⚠️ Чухал: Refactoring-ийн ХАМГИЙН ЧУХАЛ алхам нь тест. Тестгүйгээр refactoring хийх нь нүдээ аниад газар хөдлөлтийн бүсэд барилга барьсантай адил.
1.6 Refactoring ба SOLID зарчмууд
| SOLID зарчим | Refactoring холбоос |
|---|---|
| S — Single Responsibility | Extract Class, Extract Method → Нэг үүрэг |
| O — Open/Closed | Replace Conditional with Polymorphism → Шинэ төрөл нэмэхэд хаалттай |
| L — Liskov Substitution | Refused Bequest засах → Зөв удамшил |
| I — Interface Segregation | Extract Interface → Жижиг, тусгай интерфейс |
| D — Dependency Inversion | Introduce Parameter → Абстрак хамаарал |
1.7 IDE Refactoring хэрэгслүүд
Орчин үеийн IDE-нүүд refactoring-ийг автоматаар хийх хэрэгслүүдтэй.
Eclipse IDE-ийн refactoring товчлолууд:
| Үйлдэл | Windows/Linux | Mac |
|---|---|---|
| Rename | Alt+Shift+R | Cmd+Option+R |
| Extract Method | Alt+Shift+M | Cmd+Option+M |
| Extract Variable | Alt+Shift+L | Cmd+Option+L |
| Extract Constant | Ctrl+1 → Extract Constant | Cmd+1 → Extract Constant |
| Inline | Alt+Shift+I | Cmd+Option+I |
| Move | Alt+Shift+V | Cmd+Option+V |
| Refactor Menu | Alt+Shift+T | Cmd+Option+T |
💡 Зөвлөгөө: IDE-ийн refactoring хэрэгсэл ашиглах нь гараар өөрчлөх дүрмаас найдвартай — IDE нь бүх reference-ийг автоматаар шинэчилдэг.
1.8 Refactoring-ийн жишээ: Бүтэн код
Өмнө (Refactoring хийхийн ӨМНӨ):
class OrderProcessor {
public double proc(String ct, List<String[]> items, String dc) {
double t = 0;
// items = [name, price, qty]
for (String[] i : items) {
double p = Double.parseDouble(i[1]);
int q = Integer.parseInt(i[2]);
t += p * q;
}
// tax
if (ct.equals("MN")) {
t = t + t * 0.1;
} else if (ct.equals("US")) {
t = t + t * 0.08;
}
// discount
if (dc != null) {
if (dc.equals("VIP")) {
t = t * 0.9;
} else if (dc.equals("STUDENT")) {
t = t * 0.8;
}
}
if (t > 100000) {
t = t * 0.95;
}
return t;
}
}
Дараа (Refactoring хийсний ДАРАА):
class OrderProcessor {
private static final double BULK_DISCOUNT_THRESHOLD = 100_000;
private static final double BULK_DISCOUNT_RATE = 0.95;
public double calculateTotal(String countryCode, List<OrderItem> items,
DiscountType discountType) {
double subtotal = calculateSubtotal(items);
double totalWithTax = applyTax(subtotal, countryCode);
double totalWithDiscount = applyMemberDiscount(totalWithTax, discountType);
return applyBulkDiscount(totalWithDiscount);
}
private double calculateSubtotal(List<OrderItem> items) {
return items.stream()
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}
private double applyTax(double amount, String countryCode) {
double taxRate = TaxCalculator.getTaxRate(countryCode);
return amount * (1 + taxRate);
}
private double applyMemberDiscount(double amount, DiscountType discountType) {
if (discountType == null) return amount;
return amount * discountType.getRate();
}
private double applyBulkDiscount(double amount) {
if (amount > BULK_DISCOUNT_THRESHOLD) {
return amount * BULK_DISCOUNT_RATE;
}
return amount;
}
}
class OrderItem {
private String name;
private double price;
private int quantity;
// getters
}
enum DiscountType {
VIP(0.9),
STUDENT(0.8);
private final double rate;
DiscountType(double rate) { this.rate = rate; }
public double getRate() { return rate; }
}
class TaxCalculator {
private static final Map<String, Double> TAX_RATES = Map.of(
"MN", 0.1,
"US", 0.08
);
public static double getTaxRate(String countryCode) {
return TAX_RATES.getOrDefault(countryCode, 0.0);
}
}
Хийсэн refactoring-ууд:
| # | Refactoring | Тайлбар |
|---|---|---|
| 1 | Rename | proc → calculateTotal, t → subtotal, ct → countryCode |
| 2 | Extract Method | calculateSubtotal(), applyTax(), applyMemberDiscount(), applyBulkDiscount() |
| 3 | Replace Magic Number | 100000 → BULK_DISCOUNT_THRESHOLD, 0.95 → BULK_DISCOUNT_RATE |
| 4 | Introduce Parameter Object | String[] → OrderItem класс |
| 5 | Replace Conditional with Polymorphism | if/else discount → DiscountType enum |
| 6 | Extract Class | Татварын логик → TaxCalculator класс |
ХЭСЭГ 2: ТҮЛХҮҮР ҮГ БА МЭРГЭЖЛИЙН НЭР ТОМЬЁО (Keywords & Glossary)
| # | Англи нэр томьёо | Монгол утга | Дэлгэрэнгүй тайлбар |
|---|---|---|---|
| 1 | Refactoring | Дахин засварлалт | Гадаад зан төлөвийг өөрчлөхгүйгээр кодын дотоод бүтцийг сайжруулах. |
| 2 | Code Smell | Кодын үнэр | Кодонд асуудал байж болзошгүйг илтгэдэг шинж тэмдэг. Алдаа биш, дохио. |
| 3 | Technical Debt | Техникийн өр | Хурдан гэхдээ бохир код бичсэнээс үүдсэн ирээдүйн засвар зардал. |
| 4 | Extract Method | Метод гаргах | Код блокийг тусдаа методод гаргах. Урт методыг хуваах. |
| 5 | Extract Class | Класс гаргах | Нэг классын нэмэлт үүргийг тусдаа класст гаргах. |
| 6 | Rename | Нэр өөрчлөх | Хувьсагч, метод, классын нэрийг утга учиртай болгох. |
| 7 | Inline Method | Метод оруулах | Хэт энгийн методын биеийг дуудагч кодонд оруулах. Extract Method-ийн эсрэг. |
| 8 | Move Method | Метод зөөх | Методыг илүү тохиромжтой класс руу зөөх. |
| 9 | Replace Magic Number | Шидэт тоо солих | Утга учиргүй тоог нэрлэсэн тогтмолоор солих. 18 → LEGAL_AGE |
| 10 | Long Method | Урт метод | Хэт урт метод — олон зүйл хийж, ойлгоход хэцүү. |
| 11 | Large Class | Том класс | Хэт олон үүрэг хариуцсан класс (SRP зөрчил). |
| 12 | Duplicate Code | Давхардсан код | Ижил эсвэл маш төстэй код олон газар давтагдах. |
| 13 | Dead Code | Үхсэн код | Хэзээ ч ажиллахгүй, дуудагдахгүй код. |
| 14 | Primitive Obsession | Анхдагч төрлийн хэрэглээ | String, int-ийг хэт их ашиглаж, утга бүхий объект үүсгэхгүй байх. |
| 15 | Long Parameter List | Олон параметр | Метод хэт олон параметр авч байна. |
| 16 | Feature Envy | Бусдын шинж чанарт шунах | Метод өөрийнхөөсөө илүү бусад классын өгөгдлийг ашиглаж байна. |
| 17 | Message Chains | Мессежийн гинж | a.getB().getC().getD() — олон объектоор дамжин дуудах. |
| 18 | Switch Statements | Switch давтамж | Ижил switch/if-else олон газар давтагдах. |
| 19 | Refused Bequest | Татгалзсан өв | Дэд класс эх классын ихэнх зүйлийг ашигладаггүй. |
| 20 | Shotgun Surgery | Сум шиг тархалт | Нэг өөрчлөлт хийхэд олон класст засвар шаардлагатай. |
| 21 | Divergent Change | Салангид өөрчлөлт | Нэг класст олон өөр шалтгаанаар өөрчлөлт хийх. |
| 22 | Speculative Generality | Шаардлагагүй ерөнхийлөлт | Ирээдүйд хэрэг болно гэж шаардлагагүй абстракци үүсгэсэн. |
| 23 | Parameter Object | Параметрийн объект | Олон параметрийг нэг объектод цуглуулах. |
| 24 | Value Object | Утга объект | Утга бүхий жижиг объект. PhoneNumber, Email, Address зэрэг. |
| 25 | Replace Conditional with Polymorphism | Нөхцөлийг полиморфизмаар солих | switch/if-else-ийг удамшил, полиморфизмаар солих. |
| 26 | Replace Temp with Query | Түр хувьсагчийг query-гээр солих | Түр хувьсагчийн оронд метод дуудлага ашиглах. |
| 27 | Introduce Parameter | Параметр нэмэх | Метод дотор хатуу утга (hardcode) байгааг параметрээр дамжуулах. |
| 28 | Hide Delegate | Делегат нуух | Message chain-ийг delegate метод ашиглан богиносгох. |
| 29 | Self-Documenting Code | Өөрийгөө тайлбарлагч код | Нэр, бүтцээрээ тайлбаргүйгээр ойлгогддог код. |
| 30 | Rule of Three | Гурвын дүрэм | 3 дахь удаа давтагдвал refactor хий. |
| 31 | Boy Scout Rule | Хөвгүүд скаутын дүрэм | Кодыг олсноосоо илүү цэвэр орхи. |
| 32 | Code Decay | Кодын ялзрал | Цаг хугацаа өнгөрөхтүтэй кодын бүтэц муудах. |
| 33 | Code Review | Кодын шалгалт | Бусад хөгжүүлэгчийн кодыг шалгаж сайжруулах. |
| 34 | Inline Variable | Хувьсагч оруулах | Шаардлагагүй түр хувьсагчийг арилгаж шууд утга ашиглах. |
| 35 | Extract Interface | Интерфейс гаргах | Классаас интерфейс тусгаарлах (ISP зарчим). |
| 36 | Pull Up Method | Метод дээш зөөх | Дэд классуудын нийтлэг методыг эх класс руу зөөх. |
| 37 | Push Down Method | Метод доош зөөх | Эх классын тусгай методыг зөвхөн хэрэглэдэг дэд класс руу зөөх. |
| 38 | Encapsulate Field | Талбар нуух | public талбарыг private болгож, getter/setter нэмэх. |
| 39 | Compose Method | Метод найруулах | Метод дотор ижил абстракцийн түвшний алхмуудыг бүлэглэх. |
| 40 | Martin Fowler | Мартин Фаулер | "Refactoring" номны зохиогч. Refactoring-ийн каталог бүтээсэн. |
ХЭСЭГ 3: ЛАБОРАТОРИ БА ПРАКТИК ЗААВАР (Labs & Step-by-Step Guide)
3.1 Лабораторийн зорилго
Энэ лабораторид та кодын code smell таних, refactoring техник хэрэглэх чадвар эзэмшинэ:
- Long Method → Extract Method
- Magic Number, Rename → Цэвэр код
- Duplicate Code → Нийтлэг метод гаргах
- Replace Conditional with Polymorphism
Шаардлагатай зүйлс: Java JDK 17+, Eclipse IDE
3.2 Лаб 1: Extract Method — Оюутны GPA тооцоолох
Даалгавар: Дараах "муу" кодыг refactor хийх.
Алхам 1: Refactoring хийхийн ӨМНӨХ код:
class StudentReportCard {
// ❌ Long Method — хэт урт, олон зүйл хийж байна
public void generateReport(String name, int[] scores) {
// GPA тооцоолох
double total = 0;
for (int score : scores) {
total += score;
}
double gpa = total / scores.length;
// Үсгэн дүн тодорхойлох
String grade;
if (gpa >= 90) grade = "A";
else if (gpa >= 80) grade = "B";
else if (gpa >= 70) grade = "C";
else if (gpa >= 60) grade = "D";
else grade = "F";
// Тэнцсэн эсэх
boolean passed = gpa >= 60;
// Тайлан хэвлэх
System.out.println("================================");
System.out.println("ОЮУТНЫ ТАЙЛАН");
System.out.println("================================");
System.out.println("Нэр: " + name);
System.out.println("Дундаж дүн: " + String.format("%.1f", gpa));
System.out.println("Үсгэн дүн: " + grade);
System.out.println("Тэнцсэн: " + (passed ? "Тийм" : "Үгүй"));
System.out.println("================================");
}
}
Алхам 2: Code Smell тодорхойлох:
| # | Code Smell | Тайлбар |
|---|---|---|
| 1 | Long Method | Нэг метод дотор GPA тооцох, дүн тодорхойлох, хэвлэх бүгд байна |
| 2 | Magic Number | 90, 80, 70, 60 — хатуу утгууд |
| 3 | Нэг метод олон үүрэгтэй | Тооцоолол + Дүн + Хэвлэх = 3 үүрэг |
Алхам 3: Refactoring хийсний ДАРААХ код:
class StudentReportCard {
private static final int GRADE_A_THRESHOLD = 90;
private static final int GRADE_B_THRESHOLD = 80;
private static final int GRADE_C_THRESHOLD = 70;
private static final int PASSING_THRESHOLD = 60;
public void generateReport(String name, int[] scores) {
double gpa = calculateGPA(scores);
String grade = determineGrade(gpa);
boolean passed = isPassing(gpa);
printReport(name, gpa, grade, passed);
}
private double calculateGPA(int[] scores) {
double total = 0;
for (int score : scores) {
total += score;
}
return total / scores.length;
}
private String determineGrade(double gpa) {
if (gpa >= GRADE_A_THRESHOLD) return "A";
if (gpa >= GRADE_B_THRESHOLD) return "B";
if (gpa >= GRADE_C_THRESHOLD) return "C";
if (gpa >= PASSING_THRESHOLD) return "D";
return "F";
}
private boolean isPassing(double gpa) {
return gpa >= PASSING_THRESHOLD;
}
private void printReport(String name, double gpa, String grade, boolean passed) {
System.out.println("================================");
System.out.println("ОЮУТНЫ ТАЙЛАН");
System.out.println("================================");
System.out.println("Нэр: " + name);
System.out.println("Дундаж дүн: " + String.format("%.1f", gpa));
System.out.println("Үсгэн дүн: " + grade);
System.out.println("Тэнцсэн: " + (passed ? "Тийм" : "Үгүй"));
System.out.println("================================");
}
}
Алхам 4: Хийсэн refactoring-ийг тодорхойлох:
| Refactoring | Хийсэн зүйл |
|---|---|
| Extract Method | calculateGPA(), determineGrade(), isPassing(), printReport() |
| Replace Magic Number | 90 → GRADE_A_THRESHOLD, 60 → PASSING_THRESHOLD |
3.3 Лаб 2: Duplicate Code арилгах — Хөнгөлөлт тооцоолох
Алхам 1: ӨМНӨХ код (Duplicate Code smell):
class DiscountCalculator {
// ❌ Duplicate Code — ижил логик 3 газар давтагдаж байна
public double calculateStudentDiscount(double price, int quantity) {
double subtotal = price * quantity;
double tax = subtotal * 0.1;
double discount = subtotal * 0.2; // Оюутан 20%
double total = subtotal + tax - discount;
if (total < 0) total = 0;
return total;
}
public double calculateTeacherDiscount(double price, int quantity) {
double subtotal = price * quantity;
double tax = subtotal * 0.1;
double discount = subtotal * 0.15; // Багш 15%
double total = subtotal + tax - discount;
if (total < 0) total = 0;
return total;
}
public double calculateVIPDiscount(double price, int quantity) {
double subtotal = price * quantity;
double tax = subtotal * 0.1;
double discount = subtotal * 0.25; // VIP 25%
double total = subtotal + tax - discount;
if (total < 0) total = 0;
return total;
}
}
Алхам 2: Code Smell тодорхойлох:
| # | Code Smell | Тайлбар |
|---|---|---|
| 1 | Duplicate Code | 3 метод бараг ИЖИЛ, зөвхөн discount rate өөр |
| 2 | Magic Number | 0.1, 0.2, 0.15, 0.25 хатуу утгууд |
Алхам 3: ДАРААХ код (Refactored):
class DiscountCalculator {
private static final double TAX_RATE = 0.1;
enum CustomerType {
STUDENT(0.2),
TEACHER(0.15),
VIP(0.25);
private final double discountRate;
CustomerType(double discountRate) { this.discountRate = discountRate; }
public double getDiscountRate() { return discountRate; }
}
public double calculateTotal(double price, int quantity, CustomerType customerType) {
double subtotal = calculateSubtotal(price, quantity);
double tax = calculateTax(subtotal);
double discount = calculateDiscount(subtotal, customerType.getDiscountRate());
return Math.max(0, subtotal + tax - discount);
}
private double calculateSubtotal(double price, int quantity) {
return price * quantity;
}
private double calculateTax(double amount) {
return amount * TAX_RATE;
}
private double calculateDiscount(double amount, double rate) {
return amount * rate;
}
}
Алхам 4: Хийсэн refactoring:
| Refactoring | Хийсэн зүйл |
|---|---|
| Extract Method | calculateSubtotal(), calculateTax(), calculateDiscount() |
| Replace Magic Number | 0.1 → TAX_RATE, discount rate-ууд → enum |
| Parameterize Method | 3 ижил метод → 1 параметрчилсэн метод |
| Replace Type Code with Enum | Discount rate → CustomerType enum |
3.4 Лаб 3: Replace Conditional with Polymorphism — Дүрс
Алхам 1: ӨМНӨХ код (Switch statement smell):
class ShapeCalculator {
// ❌ Switch smell — шинэ дүрс нэмэх бүрд энд засвар хийх шаардлагатай
public double calculateArea(String type, double... params) {
switch (type) {
case "circle":
return Math.PI * params[0] * params[0];
case "rectangle":
return params[0] * params[1];
case "triangle":
return 0.5 * params[0] * params[1];
default:
throw new IllegalArgumentException("Үл мэдэгдэх дүрс: " + type);
}
}
public double calculatePerimeter(String type, double... params) {
switch (type) {
case "circle":
return 2 * Math.PI * params[0];
case "rectangle":
return 2 * (params[0] + params[1]);
case "triangle":
return params[0] + params[1] + params[2];
default:
throw new IllegalArgumentException("Үл мэдэгдэх дүрс: " + type);
}
}
}
Алхам 2: ДАРААХ код (Polymorphism ашигласан):
// Интерфейс
interface Shape {
double calculateArea();
double calculatePerimeter();
String getName();
}
// Тойрог
class Circle implements Shape {
private final double radius;
public Circle(double radius) { this.radius = radius; }
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * radius;
}
@Override
public String getName() { return "Тойрог"; }
}
// Тэгш өнцөгт
class Rectangle implements Shape {
private final double width;
private final double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() { return width * height; }
@Override
public double calculatePerimeter() { return 2 * (width + height); }
@Override
public String getName() { return "Тэгш өнцөгт"; }
}
// Гурвалжин
class Triangle implements Shape {
private final double base;
private final double height;
private final double sideA, sideB, sideC;
public Triangle(double base, double height, double sideA, double sideB, double sideC) {
this.base = base; this.height = height;
this.sideA = sideA; this.sideB = sideB; this.sideC = sideC;
}
@Override
public double calculateArea() { return 0.5 * base * height; }
@Override
public double calculatePerimeter() { return sideA + sideB + sideC; }
@Override
public String getName() { return "Гурвалжин"; }
}
// Хэрэглээ
class ShapeReport {
public void printReport(List<Shape> shapes) {
for (Shape shape : shapes) {
System.out.println(shape.getName() + ":");
System.out.printf(" Талбай: %.2f%n", shape.calculateArea());
System.out.printf(" Периметр: %.2f%n", shape.calculatePerimeter());
}
}
}
Алхам 3: Хийсэн refactoring:
| Refactoring | Хийсэн зүйл |
|---|---|
| Replace Conditional with Polymorphism | switch → Shape интерфейс + дэд класс |
| Extract Class | Circle, Rectangle, Triangle тусдаа класс |
| Open/Closed Principle | Шинэ дүрс нэмэхэд зөвхөн шинэ класс нэмнэ, хуучин код өөрчлөхгүй |
3.5 Лаб 4: Бүтэн Refactoring дасгал — Номын сан
Алхам 1: ӨМНӨХ код (олон smell):
class Library {
List<String[]> books = new ArrayList<>(); // [title, author, year, available]
public void doStuff(String action, String t, String a, String y) {
if (action.equals("add")) {
books.add(new String[]{t, a, y, "true"});
System.out.println("Нэмэгдлээ: " + t);
} else if (action.equals("find")) {
for (String[] b : books) {
if (b[0].toLowerCase().contains(t.toLowerCase())) {
System.out.println(b[0] + " - " + b[1] + " (" + b[2] + ") " +
(b[3].equals("true") ? "Бэлэн" : "Зээлсэн"));
}
}
} else if (action.equals("borrow")) {
for (String[] b : books) {
if (b[0].equals(t) && b[3].equals("true")) {
b[3] = "false";
System.out.println("Зээллээ: " + t);
return;
}
}
System.out.println("Олдсонгүй эсвэл зээлсэн байна");
} else if (action.equals("return")) {
for (String[] b : books) {
if (b[0].equals(t) && b[3].equals("false")) {
b[3] = "true";
System.out.println("Буцааллаа: " + t);
return;
}
}
}
}
}
Алхам 2: Code Smell жагсаалт:
| # | Code Smell | Тайлбар |
|---|---|---|
| 1 | Long Method | doStuff() нэг метод бүх зүйл хийж байна |
| 2 | Primitive Obsession | String[] ашиглаж, Book объект үүсгээгүй |
| 3 | Magic String | "add", "find", "borrow", "true", "false" |
| 4 | Нэрлэлт муу | doStuff, t, a, y, b |
| 5 | God Method | Нэг метод бүх логик агуулж байна |
Алхам 3: ДАРААХ код (бүрэн refactored):
class Book {
private String title;
private String author;
private int year;
private boolean available;
public Book(String title, String author, int year) {
this.title = title;
this.author = author;
this.year = year;
this.available = true;
}
public boolean isAvailable() { return available; }
public void markAsBorrowed() { this.available = false; }
public void markAsReturned() { this.available = true; }
public String getTitle() { return title; }
public String getAuthor() { return author; }
public int getYear() { return year; }
public boolean matchesTitle(String keyword) {
return title.toLowerCase().contains(keyword.toLowerCase());
}
@Override
public String toString() {
String status = available ? "Бэлэн" : "Зээлсэн";
return title + " - " + author + " (" + year + ") [" + status + "]";
}
}
class Library {
private final List<Book> books = new ArrayList<>();
public void addBook(String title, String author, int year) {
Book book = new Book(title, author, year);
books.add(book);
System.out.println("Нэмэгдлээ: " + book.getTitle());
}
public List<Book> findBooks(String keyword) {
List<Book> results = new ArrayList<>();
for (Book book : books) {
if (book.matchesTitle(keyword)) {
results.add(book);
}
}
return results;
}
public boolean borrowBook(String title) {
for (Book book : books) {
if (book.getTitle().equals(title) && book.isAvailable()) {
book.markAsBorrowed();
System.out.println("Зээллээ: " + title);
return true;
}
}
System.out.println("Олдсонгүй эсвэл зээлсэн байна: " + title);
return false;
}
public boolean returnBook(String title) {
for (Book book : books) {
if (book.getTitle().equals(title) && !book.isAvailable()) {
book.markAsReturned();
System.out.println("Буцааллаа: " + title);
return true;
}
}
System.out.println("Олдсонгүй: " + title);
return false;
}
public void printSearchResults(String keyword) {
List<Book> results = findBooks(keyword);
if (results.isEmpty()) {
System.out.println("Олдсонгүй: " + keyword);
} else {
results.forEach(System.out::println);
}
}
}
Алхам 4: Хийсэн refactoring жагсаалт:
| # | Refactoring | Тайлбар |
|---|---|---|
| 1 | Extract Class | String[] → Book класс |
| 2 | Extract Method | doStuff() → addBook(), findBooks(), borrowBook(), returnBook() |
| 3 | Rename | doStuff → тодорхой нэрүүд, t → title, b → book |
| 4 | Replace Magic String | "true"/"false" → boolean available |
| 5 | Encapsulate Field | List<String[]> books → private List<Book> books |
| 6 | Move Method | matchesTitle(), toString() → Book класс руу |
ХЭСЭГ 4: ШАЛГАЛТЫН АСУУЛТ (Knowledge Check — 100 тест)
Тест 1
Refactoring гэж юу вэ?
- A) Шинэ функц нэмэх
- B) Гадаад зан төлөвийг өөрчлөхгүйгээр кодын дотоод бүтцийг сайжруулах
- C) Алдаа засах
- D) Кодыг бүрэн дахин бичих
Зөв хариулт: B
Тайлбар: Refactoring = Гадаад зан төлөв ИЖИЛ, дотоод бүтэц САЙЖИРНА. Байшинг гаднаас ижил, дотор засварласан шиг.
Тест 2
Refactoring хийхийн ХАМГИЙН ЧУХАЛ урьдчилсан нөхцөл юу вэ?
- A) IDE суулгасан байх
- B) Тест бичсэн байх
- C) Менежерийн зөвшөөрөл авах
- D) Баримт бичиг бичсэн байх
Зөв хариулт: B
Тайлбар: Тестгүйгээр refactoring хийх нь нүдээ аниад барилга засварласантай адил. Тест нь гадаад зан төлөв өөрчлөгдөөгүйг БАТАЛГААЖУУЛДАГ.
Тест 3
Code Smell гэж юу вэ?
- A) Кодын алдаа
- B) Кодонд асуудал байж болзошгүйг илтгэдэг шинж тэмдэг
- C) Кодын хурд удаашрал
- D) Компайлын алдаа
Зөв хариулт: B
Тайлбар: Code Smell = Кодын "муухай үнэр". Алдаа биш, гэхдээ кодын чанар МУУДАЖ байгаагийн дохио. Хоолны хадгалах хугацаа дуусаагүй ч үнэртэж байвал асуудалтай.
Тест 4
Technical Debt (Техникийн өр) гэж юу вэ?
- A) Компанийн мөнгөний өр
- B) Хурдан бохир код бичсэнээс үүдсэн ирээдүйн засвар зардал
- C) Хөгжүүлэгчийн цалин
- D) Сервер зардал
Зөв хариулт: B
Тайлбар: Technical Debt = Банкны зээл шиг. Одоо хурдан бохир код бичвэл (зээл), ирээдүйд ХҮҮТЭЙГЭЭР буцааж засах (өр төлөх) хэрэгтэй.
Тест 5
Дараах аль нь "Long Method" code smell вэ?
- A) 5 мөр метод
- B) 200+ мөр метод олон зүйл хийж байна
- C) Нэг зүйл хийдэг 20 мөр метод
- D) Static метод
Зөв хариулт: B
Тайлбар: Long Method нь хэт олон мөр, олон үүрэг хариуцсан метод. Зөвлөмж: Нэг метод 20-30 мөрнөөс ихгүй, нэг л зүйл хийх ёстой.
Тест 6
Long Method code smell-ийг ямар refactoring-ээр засах вэ?
- A) Rename
- B) Extract Method
- C) Inline Method
- D) Move Method
Зөв хариулт: B
Тайлбар: Extract Method: Урт методын тусдаа хэсгүүдийг жижиг методуудад гаргана.
processOrder()→validateCustomer(),calculateTotal(),sendReceipt().
Тест 7
"Large Class" code smell гэж юу вэ?
- A) Хэт жижиг класс
- B) Хэт олон үүрэг хариуцсан том класс
- C) Олон метод бүхий класс
- D) Abstract класс
Зөв хариулт: B
Тайлбар: Large Class = Нэг класс хэт олон үүрэг хариуцаж байна (SRP зөрчил). UserManager дотор имэйл, тайлан, нөөцлөлт бүгд байх.
Тест 8
"Duplicate Code" code smell-ийг хэрхэн засах вэ?
- A) Бүгдийг устгах
- B) Нийтлэг метод гаргаж, давхардлыг арилгах
- C) Тайлбар бичих
- D) Нэр өөрчлөх
Зөв хариулт: B
Тайлбар: Ижил кодыг нэг нийтлэг методод гаргаж, давхардлыг арилгана (Extract Method). DRY зарчим: Don't Repeat Yourself.
Тест 9
"Dead Code" гэж юу вэ?
- A) Тест код
- B) Хэзээ ч ажиллахгүй, дуудагдахгүй код
- C) Хуучирсан код
- D) Тайлбар
Зөв хариулт: B
Тайлбар: Dead Code = Үхсэн код. Хэзээ ч дуудагдахгүй метод,
if (false) {...}блок зэрэг. Устгах нь зөв — код уншихад саад болдог.
Тест 10
"Primitive Obsession" code smell гэж юу вэ?
- A) Олон класс ашиглах
- B) String, int зэрэг энгийн төрлийг хэт ашиглаж, утга бүхий объект үүсгэхгүй байх
- C) Олон параметр
- D) Урт метод
Зөв хариулт: B
Тайлбар: Утасны дугаарыг
String, хаягийгStringгэж хадгалах орондPhoneNumber,Addressкласс үүсгэх нь зөв — баталгаажуулалт, бүтэц нэмэгдэнэ.
Тест 11
"Magic Number" гэж юу вэ?
- A) Математикийн тоо
- B) Кодонд утга учиргүй хатуу бичигдсэн тоо
- C) Random тоо
- D) Тогтмол
Зөв хариулт: B
Тайлбар:
if (age >= 18)— 18 юу гэсэн утга?if (age >= LEGAL_AGE)→ Тодорхой! Magic Number-ийг нэрлэсэн тогтмолоор солих.
Тест 12
"Feature Envy" code smell гэж юу вэ?
- A) Класс хэт том
- B) Метод өөрийн классаас илүү бусад классын өгөгдлийг ашигладаг
- C) Метод хэт урт
- D) Олон параметр
Зөв хариулт: B
Тайлбар: Метод өөрийн гэрээсээ хөршийн гэрт илүү их байдаг бол — тэр метод хөршийн гэрт (тэр класст) байх ёстой. Move Method ашиглана.
Тест 13
"Message Chains" code smell гэж юу вэ?
- A) Олон if/else
- B)
a.getB().getC().getD()гэх мэт олон объектоор дамжин дуудах - C) Олон параметр
- D) Давхардсан код
Зөв хариулт: B
Тайлбар: Message Chain = Гинж.
order.getCustomer().getAddress().getCity()— дундын объект өөрчлөгдвөл бүх chain эвдэрнэ. Hide Delegate ашиглана.
Тест 14
"Shotgun Surgery" code smell гэж юу вэ?
- A) Нэг класст олон засвар
- B) Нэг өөрчлөлт хийхэд олон класст засвар шаардлагатай
- C) Урт метод
- D) Давхардсан код
Зөв хариулт: B
Тайлбар: Shotgun Surgery = Нэг зүйл солиход олон файл/класст нэвтрэх. Жишээ: Мэдээллийн сангийн нэр солиход 20 класс засвар → Нэг газарт нэгтгэх.
Тест 15
Extract Method refactoring юу хийдэг вэ?
- A) Методыг устгах
- B) Код блокийг тусдаа шинэ метод болгон гаргах
- C) Методын нэр солих
- D) Метод нэгтгэх
Зөв хариулт: B
Тайлбар: Extract Method: Урт методын нэг хэсгийг сонгоод, тусдаа метод болгон гаргана. IDE-д: Ctrl+Alt+M (IntelliJ).
Тест 16
Inline Method refactoring юу хийдэг вэ?
- A) Шинэ метод үүсгэх
- B) Хэт энгийн методын биеийг дуудагч код руу оруулж, методыг устгах
- C) Метод нэр солих
- D) Метод зөөх
Зөв хариулт: B
Тайлбар: Inline Method нь Extract Method-ийн ЭСРЭГ.
boolean isAdult() { return age >= 18; }→ Хэт энгийн бол шуудif (age >= 18)гэж бичнэ.
Тест 17
Rename refactoring-ийн зорилго юу вэ?
- A) Код хурдасгах
- B) Хувьсагч, метод, классын нэрийг утга учиртай болгох
- C) Алдаа засах
- D) Шинэ функц нэмэх
Зөв хариулт: B
Тайлбар:
int d;→int daysSinceCreation;. Сайн нэр = Код өөрийгөө тайлбарлана (self-documenting code). IDE: Shift+F6.
Тест 18
Replace Magic Number with Named Constant юу хийдэг вэ?
- A) Тоог устгах
- B) Утга учиргүй тоог нэрлэсэн тогтмолоор солих
- C) Тоог String болгох
- D) Тоог нэмэгдүүлэх
Зөв хариулт: B
Тайлбар:
0.1→TAX_RATE,18→LEGAL_AGE. Код уншихад тодорхой, нэг газар өөрчлөхөд хангалттай.
Тест 19
Extract Class refactoring хэзээ хийх вэ?
- A) Класс хэт жижиг үед
- B) Нэг класс хэт олон үүрэг хариуцаж байх үед
- C) Класс хоосон үед
- D) Компайл алдаа гарах үед
Зөв хариулт: B
Тайлбар: Нэг класс олон үүрэг → Extract Class: Нэмэлт үүргийг тусдаа класст гаргана.
UserManager→UserService+EmailService+ReportService.
Тест 20
Replace Conditional with Polymorphism юу хийдэг вэ?
- A) if/else нэмэх
- B) switch/if-else-ийг полиморфизм (удамшил, интерфейс) ашиглан солих
- C) Нөхцөлийг устгах
- D) Нөхцөл нэмэх
Зөв хариулт: B
Тайлбар: switch дахь
"circle","rectangle"→Circle,Rectangleкласс бүхий интерфейс. Шинэ төрөл нэмэхэд зөвхөн шинэ класс нэмнэ (OCP).
Тест 21
"Rule of Three" (Гурвын дүрэм) юу гэсэн үг вэ?
- A) 3 хүн тестлэх
- B) Ижил код 3 дахь удаа давтагдвал refactor хийх
- C) 3 класс бичих
- D) 3 тест бичих
Зөв хариулт: B
Тайлбар: 1 дэх удаа — зүгээр хий. 2 дахь удаа — давтагдаж байна гэж анзаар. 3 дахь удаа — REFACTOR хий! (Don Roberts)
Тест 22
"Boy Scout Rule" юу гэсэн үг вэ?
- A) Тест бичих
- B) Кодыг олсноосоо илүү цэвэр орхих
- C) Шинэ функц нэмэх
- D) Баримт бичиг бичих
Зөв хариулт: B
Тайлбар: Boy Scout Rule = "Хүрч ирснээсээ илүү цэвэр орхи." Код дотор ажиллаж байхдаа жижиг refactoring хийж, кодыг сайжруулж орхих.
Тест 23
Refactoring ба Bug Fix-ийн ялгаа юу вэ?
- A) Ялгаагүй
- B) Refactoring нь бүтцийг сайжруулна, Bug Fix нь алдааг засна
- C) Bug Fix нь бүтцийг сайжруулна
- D) Refactoring нь алдааг засна
Зөв хариулт: B
Тайлбар: Refactoring: Гадаад зан төлөв ИЖИЛ, бүтэц САЙЖИРНА. Bug Fix: Гадаад зан төлөв ӨӨРЧЛӨГДӨНӨ (буруу → зөв).
Тест 24
Дараах аль нь refactoring БИШ вэ?
- A) Extract Method
- B) Rename
- C) Шинэ функц нэмэх
- D) Replace Magic Number
Зөв хариулт: C
Тайлбар: Шинэ функц нэмэх нь гадаад зан төлөвийг ӨӨРЧИЛДӨГ → Refactoring БИШ. Refactoring = зан төлөв ижил, бүтэц сайжирна.
Тест 25
"Speculative Generality" code smell гэж юу вэ?
- A) Хэрэгтэй абстракци
- B) Ирээдүйд хэрэг болно гэж шаардлагагүй абстракци үүсгэсэн
- C) Хэт жижиг класс
- D) Олон тест
Зөв хариулт: B
Тайлбар: "Ирээдүйд хэрэг болно" гэж шаардлагагүй интерфейс, абстракт класс, параметр нэмсэн. YAGNI зарчим: You Aren't Gonna Need It.
Тест 26
Refactoring ба Optimization-ийн ялгаа юу вэ?
- A) Ялгаагүй
- B) Refactoring нь бүтцийг, Optimization нь гүйцэтгэлийг сайжруулна
- C) Optimization нь бүтцийг сайжруулна
- D) Refactoring нь хурдыг нэмэгдүүлнэ
Зөв хариулт: B
Тайлбар: Refactoring: Код ОЙЛГОМЖТОЙ, засвар ХЯЛБАР болно. Optimization: Код ХУРДАН ажиллана. Зарим үед optimization нь кодыг илүү нарийн болгодог.
Тест 27
IntelliJ IDEA-д Extract Method товчлол юу вэ?
- A) Ctrl+C
- B) Ctrl+Alt+M (Windows) / Cmd+Option+M (Mac)
- C) Shift+F6
- D) F6
Зөв хариулт: B
Тайлбар: IntelliJ: Ctrl+Alt+M (Windows/Linux), Cmd+Option+M (Mac). Код сонгоод товчлол дарахад шинэ метод автоматаар үүснэ.
Тест 28
IntelliJ IDEA-д Rename товчлол юу вэ?
- A) Ctrl+R
- B) Shift+F6
- C) Ctrl+Alt+M
- D) F5
Зөв хариулт: B
Тайлбар: Shift+F6 = Rename. Нэр дээр cursor тавиад Shift+F6 дарахад бүх reference автоматаар шинэчлэгдэнэ.
Тест 29
Introduce Parameter Object юу хийдэг вэ?
- A) Параметр устгах
- B) Олон параметрийг нэг объектод цуглуулах
- C) Параметр нэмэх
- D) Параметрын төрөл солих
Зөв хариулт: B
Тайлбар: 7 параметртэй метод → 1
Configобъект.createEvent(name, desc, start, end, ...)→createEvent(EventConfig config).
Тест 30
Move Method refactoring хэзээ хийх вэ?
- A) Метод хэт богино үед
- B) Метод өөрийн классаас илүү бусад классын өгөгдлийг ашиглаж байх үед
- C) Метод хэт урт үед
- D) Метод static үед
Зөв хариулт: B
Тайлбар: Feature Envy → Move Method. Метод хөршийн гэрт илүү их байдаг бол хөршийн гэрт (тэр класст) зөөх.
Тест 31
"Refused Bequest" code smell гэж юу вэ?
- A) Класс хэт том
- B) Дэд класс эх классын ихэнх метод/шинж чанарыг ашигладаггүй
- C) Давхардсан код
- D) Урт метод
Зөв хариулт: B
Тайлбар: Аавынхаа бүх тариалангийн багажийг "өвлөж" авсан ч ашигладаггүй → Буруу удамшил. Composition ашиглах, эсвэл удамшлын бүтцийг өөрчлөх.
Тест 32
"Divergent Change" code smell гэж юу вэ?
- A) Олон класс өөрчлөгдөх
- B) Нэг класст олон өөр шалтгаанаар засвар хийх шаардлагатай
- C) Нэг засвар олон класст нөлөөлөх
- D) Код хэт урт
Зөв хариулт: B
Тайлбар: Нэг класст DB өөрчлөхөд ч, UI өөрчлөхөд ч, бизнес логик өөрчлөхөд ч засвар хийх → Олон үүрэгтэй → Extract Class.
Тест 33
Refactoring-ийн Martin Fowler хэн бэ?
- A) Java-ийн зохиогч
- B) "Refactoring" номны зохиогч, refactoring каталог бүтээсэн
- C) Design Patterns номны зохиогч
- D) TDD-ийн зохиогч
Зөв хариулт: B
Тайлбар: Martin Fowler нь "Refactoring: Improving the Design of Existing Code" (1999, 2018) номны зохиогч. 60+ refactoring техникийн каталог бүтээсэн.
Тест 34
Hide Delegate refactoring юу хийдэг вэ?
- A) Методыг нуух
- B) Message Chain (a.getB().getC()) богиносгож delegate метод ашиглах
- C) Классыг нуух
- D) Талбарыг нуух
Зөв хариулт: B
Тайлбар:
order.getCustomer().getAddress().getCity()→order.getCustomerCity(). Дундын объектуудыг нууж, delegate метод ашиглана.
Тест 35
Encapsulate Field юу хийдэг вэ?
- A) Класс устгах
- B) public талбарыг private болгож, getter/setter нэмэх
- C) Метод устгах
- D) Нэр солих
Зөв хариулт: B
Тайлбар:
public String name;→private String name; public String getName() {...}. Шууд хандалт хаагдаж, хяналт сайжирна.
Тест 36
Pull Up Method юу хийдэг вэ?
- A) Методыг устгах
- B) Дэд классуудын нийтлэг методыг эх класс руу зөөх
- C) Метод нэмэх
- D) Метод нэр солих
Зөв хариулт: B
Тайлбар: Хэрэв
Dog.eat()баCat.eat()ижил бол →Animal.eat()руу зөөх. Давхардал арилна.
Тест 37
Push Down Method юу хийдэг вэ?
- A) Методыг устгах
- B) Эх классын методыг зөвхөн хэрэглэдэг дэд класс руу зөөх
- C) Метод нэр солих
- D) Шинэ метод үүсгэх
Зөв хариулт: B
Тайлбар:
Animal.fly()→ ЗөвхөнBirdашигладаг болBird.fly()руу зөөх. Бусад дэд класст шаардлагагүй метод байхгүй болно.
Тест 38
Replace Temp with Query юу хийдэг вэ?
- A) Тест устгах
- B) Түр хувьсагчийн оронд метод дуудлага ашиглах
- C) Query бичих
- D) Тест нэмэх
Зөв хариулт: B
Тайлбар:
double base = qty * price; if (base > 1000)...→if (getBasePrice() > 1000).... Түр хувьсагч арилж, метод дуудлага ашиглана.
Тест 39
Self-Documenting Code гэж юу вэ?
- A) Тайлбар олон бичсэн код
- B) Нэр, бүтцээрээ тайлбаргүйгээр ойлгогддог код
- C) Баримт бичигтэй код
- D) Тест бүхий код
Зөв хариулт: B
Тайлбар:
boolean chk(Integer a)→boolean isAdult(Integer age). Код өөрөө тайлбарлагдаж байна — нэмэлт тайлбар шаардлагагүй.
Тест 40
Дараах кодонд ямар code smell байна?
public double calc(double p, double q, double t, double d) {
return (p * q) + (p * q * t) - (p * q * d);
}
- A) Long Method
- B) Муу нэрлэлт (Rename шаардлагатай) + Magic Number шиг параметрүүд
- C) Dead Code
- D) Code smell байхгүй
Зөв хариулт: B
Тайлбар:
p,q,t,d— утга учиргүй нэрүүд.price,quantity,taxRate,discountRateгэж нэрлэх ёстой. Rename refactoring хэрэгтэй.
Тест 41
Дараах кодонд ямар code smell байна?
class Employee {
String name;
double salary;
void printPaySlip() { ... }
void saveToDatabase() { ... }
void sendEmail() { ... }
void generateTaxReport() { ... }
}
- A) Long Method
- B) Large Class — хэт олон үүрэг
- C) Dead Code
- D) Duplicate Code
Зөв хариулт: B
Тайлбар: Employee класс цалин тооцох, DB хадгалах, имэйл илгээх, тайлан гаргах зэргийг бүгдийг хариуцаж байна → SRP зөрчил → Extract Class.
Тест 42
Дараах кодонд ямар refactoring хийх вэ?
if (date.before(SUMMER_START) || date.after(SUMMER_END))
charge = quantity * winterRate + winterServiceCharge;
else
charge = quantity * summerRate;
- A) Rename
- B) Extract Method —
isSummer(),winterCharge(),summerCharge() - C) Inline Method
- D) Move Method
Зөв хариулт: B
Тайлбар: Нөхцөл болон тооцооллыг тусдаа методуудад гаргах → Код ойлгомжтой болно.
Тест 43
SOLID-ийн "S" (Single Responsibility) ба refactoring хэрхэн холбогдох вэ?
- A) Холбогдохгүй
- B) Large Class smell-ийг Extract Class хийх = SRP баримтлах
- C) Зөвхөн тестэд хамаатай
- D) Зөвхөн дизайн паттерн
Зөв хариулт: B
Тайлбар: SRP = Нэг класс нэг үүрэг. Large Class → Extract Class хийх нь SRP зарчмыг хэрэгжүүлэх юм.
Тест 44
SOLID-ийн "O" (Open/Closed) ба refactoring хэрхэн холбогдох вэ?
- A) Холбогдохгүй
- B) Replace Conditional with Polymorphism = OCP баримтлах
- C) Зөвхөн тестэд
- D) Зөвхөн UI-д
Зөв хариулт: B
Тайлбар: OCP = Шинэ төрөл нэмэхэд хуучин кодыг ӨӨРЧЛӨХГҮЙ. switch → Polymorphism хийснээр шинэ класс нэмэхэд хуучин код хэвээрээ.
Тест 45
Refactoring хийхдээ жижиг алхмаар хийх нь яагаад чухал вэ?
- A) Хурдан дуусна
- B) Алхам бүрийн дараа тест ажиллуулж, алдаа гарвал хурдан олно
- C) Код бага болно
- D) IDE шаарддаг
Зөв хариулт: B
Тайлбар: Жижиг алхам → Тест ажиллуулах → Pass бол дараагийн алхам. Алдаа гарвал сүүлийн жижиг алхмыг буцаах хялбар.
Тест 46
Refactoring хийхээс ЗАЙЛСХИЙХ тохиолдол аль нь вэ?
- A) Код ойлгомжгүй байх үед
- B) Deadline маш ойрхон, тест хангалтгүй байх үед
- C) Code smell олон байх үед
- D) Шинэ хөгжүүлэгч нэгдсэн үед
Зөв хариулт: B
Тайлбар: Deadline ойрхон + тест хангалтгүй → Refactoring-ийн эрсдэл их. Бүтэц эвдэх, цаг хүрэхгүй болж болно. Энэ үед техникийн өрөө хүлээн зөвшөөрч, дараа нь шийдэх.
Тест 47
Дараах аль нь Bloaters (Хэт томорсон код) ангилалд БАГТАХГҮЙ вэ?
- A) Long Method
- B) Large Class
- C) Feature Envy
- D) Primitive Obsession
Зөв хариулт: C
Тайлбар: Feature Envy нь Couplers (Хэт нягт холбоос) ангилалд багтана. Bloaters-т: Long Method, Large Class, Primitive Obsession, Long Parameter List, Data Clumps.
Тест 48
Code smell-ийн 5 үндсэн ангилал аль нь вэ?
- A) Red, Green, Blue, Yellow, Purple
- B) Bloaters, OO Abusers, Change Preventers, Dispensables, Couplers
- C) Class, Method, Field, Variable, Constant
- D) Input, Output, Process, Storage, Network
Зөв хариулт: B
Тайлбар: 5 ангилал: Bloaters (том), OO Abusers (OOP зөрчил), Change Preventers (өөрчлөлтөд саад), Dispensables (шаардлагагүй), Couplers (хэт холбоос).
Тест 49
"Data Clumps" code smell гэж юу вэ?
- A) Олон класс
- B) Ижил бүлэг өгөгдөл олон газар хамтдаа давтагдах
- C) Давхардсан код
- D) Урт метод
Зөв хариулт: B
Тайлбар:
(String street, String city, String zip)олон методод хамтдаа давтагдаж байна →Addressкласс гаргах (Extract Class).
Тест 50
"Lazy Class" code smell гэж юу вэ?
- A) Хэт том класс
- B) Хэт бага ажил хийдэг, оршин тогтнох шалтгаан муутай класс
- C) Хурдан ажилладаг класс
- D) Abstract класс
Зөв хариулт: B
Тайлбар: Lazy Class = Залхуу класс. Хэт бага үүрэг → Шаардлагагүй нарийн бүтэц. Inline Class ашиглан арилгах эсвэл бусад класстай нэгтгэх.
Тест 51
"Middle Man" code smell гэж юу вэ?
- A) Урт метод
- B) Класс нь ихэнх ажлыг бусад класст делегатлаж, өөрөө юу ч хийхгүй
- C) Давхардсан код
- D) Олон параметр
Зөв хариулт: B
Тайлбар: Класс нь бүх метод дотроо зөвхөн бусад объектын метод дуудаж байгаа бол → Middle Man. Remove Middle Man эсвэл Inline Class ашиглана.
Тест 52
"Inappropriate Intimacy" code smell гэж юу вэ?
- A) Класс хэт жижиг
- B) Хоёр класс бие биенийхээ хэт олон дотоод мэдээлэлд хандаж байна
- C) Давхардсан код
- D) Олон параметр
Зөв хариулт: B
Тайлбар: Хоёр класс бие биенийхээ private талбарт шууд хандах, хэт нягт хамааралтай → Encapsulation зөрчил. Move Method/Field ашиглан тусгаарлах.
Тест 53
Compose Method загвар юу вэ?
- A) Шинэ класс үүсгэх
- B) Метод дотор ижил абстракцийн түвшний алхмуудыг бүлэглэх
- C) Тест бичих
- D) Нэр солих
Зөв хариулт: B
Тайлбар:
processOrder()доторvalidateCustomer(),calculateTotal(),processPayment()гэх мэт ижил түвшний метод дуудлагууд. Дээд метод нь "агуулга" болж, дотоод методууд "дэлгэрэнгүй" болно.
Тест 54
Дараах кодонд ямар refactoring хийх вэ?
class Rectangle {
public double width;
public double height;
}
// Гадна кодоос:
double area = rect.width * rect.height;
- A) Extract Method
- B) Encapsulate Field + Move Method — area()-ийг Rectangle руу зөөх
- C) Rename
- D) Inline Method
Зөв хариулт: B
Тайлбар: public талбар → private + getter (Encapsulate Field). area тооцоолол →
Rectangle.area()метод болгох (Move Method). Encapsulation сайжирна.
Тест 55
DRY зарчим юу гэсэн үг вэ?
- A) Debug Rapidly Yourself
- B) Don't Repeat Yourself — Өөрийгөө бүү давт
- C) Delete Redundant YAML
- D) Design Review Yearly
Зөв хариулт: B
Тайлбар: DRY = Don't Repeat Yourself. Ижил логик нэг газар л байх ёстой. Duplicate Code → Extract Method хийх = DRY баримтлах.
Тест 56
YAGNI зарчим юу гэсэн үг вэ?
- A) Yet Another Great New Idea
- B) You Aren't Gonna Need It — Танд хэрэг болохгүй
- C) Your Application Gets New Inputs
- D) You Always Get New Issues
Зөв хариулт: B
Тайлбар: YAGNI = Ирээдүйд хэрэг болно гэж шаардлагагүй зүйл нэмж болохгүй. Speculative Generality smell-тэй холбоотой.
Тест 57
KISS зарчим юу гэсэн үг вэ?
- A) Keep It Super Simple
- B) Keep It Simple, Stupid — Энгийн байлга
- C) Know It, Study, Simplify
- D) Keep Internal Structures Safe
Зөв хариулт: B
Тайлбар: KISS = Хамгийн энгийн шийдлийг сонгох. Шаардлагагүй нарийн бүтэц, ерөнхийлөлт нэмэхгүй байх.
Тест 58
Refactoring-ийн дараа тест FAIL болвол юу хийх вэ?
- A) Тестийг устгах
- B) Refactoring-ийг буцаах (undo), алдааг олох
- C) Кодыг бүрэн дахин бичих
- D) Тестийг өөрчлөх
Зөв хариулт: B
Тайлбар: Тест fail = Гадаад зан төлөв ӨӨРЧЛӨГДСӨН → Refactoring буруу хийгдсэн. Буцааж (undo), жижиг алхмаар дахин оролдох.
Тест 59
Extract Interface refactoring юу хийдэг вэ?
- A) Класс устгах
- B) Классаас интерфейс тусгаарлах
- C) Метод устгах
- D) Нэр солих
Зөв хариулт: B
Тайлбар:
class EmailService→interface NotificationService+class EmailService implements NotificationService. Хамаарлыг абстрактад шилжүүлнэ (DIP).
Тест 60
Дараах кодонд ямар code smell байна?
// Ашиглагдаагүй метод
public void oldCalculation() {
// Хуучин тооцоолол - хэн ч дуудахгүй
}
- A) Long Method
- B) Dead Code
- C) Feature Envy
- D) Duplicate Code
Зөв хариулт: B
Тайлбар: Dead Code = Хэзээ ч дуудагдахгүй метод. Устгах нь зөв. Version control (Git) дотор хадгалагдсан тул устгахад аюулгүй.
Тест 61
Тайлбар (comments) яагаад code smell байж болох вэ?
- A) Тайлбар бүгд муу
- B) Код ойлгомжгүй учраас тайлбараар нөхсөн бол — кодыг засах нь зөв
- C) Тайлбар хэзээ ч хэрэггүй
- D) IDE тайлбарыг унших чадваргүй
Зөв хариулт: B
Тайлбар: "Муу кодыг тайлбараар нөхөхөөс илүүтэй, кодыг өөрийгөө тайлбарлагдахаар бичих." Rename + Extract Method хийвэл тайлбар шаардлагагүй болдог.
Тест 62
"Parallel Inheritance Hierarchies" code smell гэж юу вэ?
- A) Давхардсан код
- B) Нэг классын дэд класс нэмэх бүрд өөр нэг удамшлын модонд ч дэд класс нэмэх шаардлагатай
- C) Класс хэт жижиг
- D) Урт метод
Зөв хариулт: B
Тайлбар:
Employeeдэд класс нэмэхэдEmployeeTaxдэд класс ч нэмэх шаардлагатай → Зэрэгцээ удамшил. Move Method/Field ашиглан нэг модонд нэгтгэх.
Тест 63
Refactoring-ийн "catalog" гэж юу вэ?
- A) Онлайн дэлгүүр
- B) Нэрлэгдсэн refactoring техникүүдийн систематик цуглуулга
- C) Тестийн жагсаалт
- D) Code smell жагсаалт
Зөв хариулт: B
Тайлбар: Martin Fowler-ийн каталог нь 60+ refactoring техникийг нэр, тайлбар, хэзээ хийх, хэрхэн хийх загвараар бүртгэсэн лавлагаа.
Тест 64
Дараах кодонд ямар refactoring хийх вэ?
String fullName = customer.getFirstName() + " " + customer.getLastName();
String greeting = "Сайн байна уу, " + customer.getFirstName();
- A) Dead Code устгах
- B)
customer.getFullName(),customer.getGreeting()→ Move Method - C) Inline Method
- D) Rename
Зөв хариулт: B
Тайлбар: Customer-ийн өгөгдлийг гаднаас нийлүүлж байгаа → Feature Envy.
getFullName(),getGreeting()методуудыг Customer класст нэмнэ (Move Method).
Тест 65
Inline Variable refactoring юу хийдэг вэ?
- A) Шинэ хувьсагч нэмэх
- B) Шаардлагагүй түр хувьсагчийг арилгаж, шууд утга ашиглах
- C) Хувьсагч нэр солих
- D) Хувьсагч төрөл солих
Зөв хариулт: B
Тайлбар:
boolean isExpensive = price > 1000; if (isExpensive)...→if (price > 1000).... Хувьсагч нь expression-аас илүү мэдээлэл өгөхгүй бол устгах.
Тест 66
Refactoring хийхэд IDE-ийн автомат refactoring хэрэгсэл ашиглах давуу тал юу вэ?
- A) Код хурдан ажиллана
- B) Бүх reference автоматаар шинэчлэгдэж, алдаа гарах магадлал бага
- C) Тест бичих шаардлагагүй
- D) Code smell автоматаар арилна
Зөв хариулт: B
Тайлбар: IDE нь Rename хийхэд бүх файлын бүх reference-ийг автоматаар шинэчилдэг → Гараар өөрчлөхөд мартах, алдаа гаргах эрсдэл бага.
Тест 67
"Temporary Field" code smell гэж юу вэ?
- A) Олон параметр
- B) Зөвхөн зарим тохиолдолд утга авдаг, бусад үед хоосон/null байдаг талбар
- C) Давхардсан код
- D) Урт метод
Зөв хариулт: B
Тайлбар: Класс дахь талбар нь зарим метод дуудагдахад л утгатай, бусад үед null → Ойлгомжгүй. Extract Class ашиглан тусгаарлах.
Тест 68
Refactoring хийхийн өмнө Git commit хийх нь яагаад чухал вэ?
- A) Менежерт харуулах
- B) Refactoring буруу болвол хялбараар буцааж (revert) болно
- C) Код хурдасна
- D) Тест шаардлагагүй болно
Зөв хариулт: B
Тайлбар: Refactoring-ийн өмнө commit → Буруу болвол
git revertхийж өмнөх байдалд буцна. "Аюулгүй байдлын тор" (safety net).
Тест 69
Refactoring ба Rewrite-ийн ялгаа юу вэ?
- A) Ялгаагүй
- B) Refactoring нь жижиг алхмаар бүтцийг сайжруулах, Rewrite нь бүгдийг дахин бичих
- C) Rewrite нь илүү аюулгүй
- D) Refactoring нь илүү удаан
Зөв хариулт: B
Тайлбар: Refactoring = Жижиг алхмаар, тестээр баталгаажуулж, аюулгүйгээр сайжруулах. Rewrite = Бүгдийг шинээр бичих — эрсдэл их, урт хугацаатай.
Тест 70
Дараах аль нь Dispensables (Шаардлагагүй зүйлс) ангилалд багтах вэ?
- A) Long Method
- B) Dead Code, Duplicate Code, Speculative Generality
- C) Feature Envy
- D) Switch Statements
Зөв хариулт: B
Тайлбар: Dispensables = Устгаж/арилгаж болох зүйлс. Dead Code, Duplicate Code, Lazy Class, Speculative Generality, Comments.
Тест 71
Дараах аль нь Change Preventers (Өөрчлөлтөд саад) ангилалд багтах вэ?
- A) Long Method, Large Class
- B) Divergent Change, Shotgun Surgery, Parallel Inheritance Hierarchies
- C) Dead Code, Duplicate Code
- D) Feature Envy, Message Chains
Зөв хариулт: B
Тайлбар: Change Preventers: Нэг газар өөрчлөлт хийхэд олон газарт нөлөөлдөг. Divergent Change, Shotgun Surgery, Parallel Inheritance Hierarchies.
Тест 72
Дараах кодонд ямар refactoring хийх вэ?
public void sendNotification(String type, String message, String to) {
if (type.equals("email")) {
// 20 мөр имэйл илгээх логик
} else if (type.equals("sms")) {
// 15 мөр SMS илгээх логик
} else if (type.equals("push")) {
// 10 мөр push notification логик
}
}
- A) Rename
- B) Replace Conditional with Polymorphism + Extract Method
- C) Inline Method
- D) Encapsulate Field
Зөв хариулт: B
Тайлбар:
NotificationServiceинтерфейс →EmailNotification,SmsNotification,PushNotificationкласс. switch арилж, шинэ төрөл нэмэхэд хялбар.
Тест 73
"Preserve Whole Object" refactoring юу хийдэг вэ?
- A) Объект устгах
- B) Объектоос олон утга авахын оронд бүтэн объект дамжуулах
- C) Объект үүсгэх
- D) Объект хуулах
Зөв хариулт: B
Тайлбар:
method(obj.getA(), obj.getB(), obj.getC())→method(obj). Бүтэн объект дамжуулснаар параметрийн тоо буурна, ирээдүйд шинэ талбар нэмэхэд хялбар.
Тест 74
"Replace Constructor with Factory Method" юу хийдэг вэ?
- A) Constructor устгах
- B) Шууд
newашиглахын оронд Factory Method ашиглах - C) Constructor нэмэх
- D) Constructor нэр солих
Зөв хариулт: B
Тайлбар:
new Circle(5)→Shape.createCircle(5). Factory Method нь нэр бүхий метод → Уншихад ойлгомжтой, шинэ төрөл нэмэхэд уян хатан.
Тест 75
"Replace Error Code with Exception" юу хийдэг вэ?
- A) Алдааг нуух
- B) Алдааны кодыг (return -1) Exception-оор солих
- C) Exception устгах
- D) Алдааг хэвлэх
Зөв хариулт: B
Тайлбар:
return -1;→throw new InsufficientFundsException(). Exception нь алдааны утгыг тодорхой дамжуулж, алдааны боловсруулалтыг зохион байгуулна.
Тест 76
Refactoring-ийн алхмуудын зөв дараалал аль нь вэ?
- A) Код бич → Тест бич → Deploy
- B) Тест шалгах → Жижиг refactoring → Тест ажиллуулах → Давтах
- C) Бүгдийг нэг дор засах → Deploy
- D) Deploy → Тест → Засвар
Зөв хариулт: B
Тайлбар: 1) Тест бэлтгэ, 2) Code smell ол, 3) Жижиг алхмаар refactor, 4) Тест ажиллуул (pass), 5) Давт. Тест → Refactor → Тест цикл.
Тест 77
Дараах кодонд ямар code smell байна?
void processOrder(Customer c, String pn, int q, double p,
String sa, String sc, String sz,
String ba, String bc, String bz) { ... }
- A) Dead Code
- B) Long Parameter List + Primitive Obsession
- C) Duplicate Code
- D) Feature Envy
Зөв хариулт: B
Тайлбар: 10 параметр!
sa,sc,sz= shipping address,ba,bc,bz= billing address →Address shippingAddress,Address billingAddressгэж Parameter Object үүсгэх.
Тест 78
"Introduce Assertion" refactoring юу хийдэг вэ?
- A) Тест устгах
- B) Кодонд assertion нэмж, тухайн цэгт биелэх ёстой нөхцөлийг тодорхой болгох
- C) Нэр солих
- D) Метод устгах
Зөв хариулт: B
Тайлбар:
assert amount >= 0 : "Дүн сөрөг байж болохгүй";— Хөгжүүлэлтийн үед кодын нөхцөлийг тодорхой болгож, алдааг эрт олоход тусална.
Тест 79
Дараах кодонд хийсэн refactoring юу вэ?
// ӨМНӨ:
if (student.getGPA() > 3.5 && student.getCredits() > 120
&& student.getAge() < 25) { ... }
// ДАРАА:
if (student.isEligibleForHonors()) { ... }
- A) Inline Method
- B) Extract Method — нөхцөлийг тусдаа методод гаргах
- C) Rename
- D) Move Method
Зөв хариулт: B
Тайлбар: Нарийн нөхцөлийг
isEligibleForHonors()нэртэй метод болгон гаргасан. Код уншихад тодорхой болсон + Student класст зөөх нь зөв (Move Method).
Тест 80
"Separate Query from Modifier" зарчим юу вэ?
- A) Query бичих
- B) Нэг метод утга буцааж (query), нөгөө метод төлөв өөрчилж (modifier) байх ёстой
- C) Бүгдийг нэг методод хийх
- D) Тест бичих
Зөв хариулт: B
Тайлбар: Нэг метод нь ЗЭРЭГцээ утга буцааж, төлөв өөрчилж байвал → Тусгаарлах.
getAndRemove()→get()+remove(). Command Query Separation (CQS).
Тест 81
Code Review-д refactoring хэрхэн тусалдаг вэ?
- A) Тусалдаггүй
- B) Бусад хөгжүүлэгч code smell олоод, refactoring санал болгож, кодын чанар сайжирна
- C) Code Review шаардлагагүй болно
- D) Зөвхөн менежер хийдэг
Зөв хариулт: B
Тайлбар: Code Review-д баг бусдын кодын smell олоод refactoring санал болгоно → Кодын чанар байнга сайжирна, баг бүгд суралцана.
Тест 82
"Replace Type Code with Subclasses" юу хийдэг вэ?
- A) Төрлийн кодыг String болгох
- B) Төрлийн кодыг (int type) удамшлын бүтцээр (дэд класс) солих
- C) Дэд класс устгах
- D) Нэр солих
Зөв хариулт: B
Тайлбар:
employee.type = ENGINEER→class Engineer extends Employee. Төрөл тус бүрд ялгаатай зан төлөв бол дэд класс ашиглах нь зөв.
Тест 83
Refactoring хийхдээ feature branch ашиглах давуу тал юу вэ?
- A) Код хурдасна
- B) Refactoring main branch-д нөлөөлөхгүй, дууссаны дараа merge хийнэ
- C) Тест шаардлагагүй
- D) Бүх хүн зэрэг ажиллана
Зөв хариулт: B
Тайлбар: Feature branch-д refactoring хийснээр main/develop branch тогтвортой. Refactoring дуусаж, тест pass болсны дараа merge хийнэ.
Тест 84
"Introduce Explaining Variable" юу хийдэг вэ?
- A) Хувьсагч устгах
- B) Нарийн expression-д утга учиртай нэртэй хувьсагч оноож, ойлгомжтой болгох
- C) Хувьсагч нэр солих
- D) Хувьсагч төрөл солих
Зөв хариулт: B
Тайлбар:
if (platform.toUpperCase().indexOf("MAC") > -1 && browser.toUpperCase().indexOf("IE") > -1)→boolean isMacIE = .... Нэрлэсэн хувьсагч ойлголтыг тодорхой болгоно.
Тест 85
Refactoring болон TDD хэрхэн хамтран ажилладаг вэ?
- A) Хамааралгүй
- B) TDD-ийн Red-Green-Refactor цикл дэх REFACTOR алхам нь refactoring хийх үе
- C) TDD нь refactoring шаарддаггүй
- D) Refactoring нь TDD-ийг шаарддаг
Зөв хариулт: B
Тайлбар: TDD = Red (тест бич, fail) → Green (код бич, pass) → REFACTOR (код цэвэрлэ). Refactoring нь TDD-ийн салшгүй хэсэг.
Тест 86
Дараах кодонд ямар refactoring хийх вэ?
class Order {
double getPrice() {
double primary = basePrice();
double secondary = secondaryPrice();
double tertiary = tertiaryPrice();
return primary + secondary + tertiary;
}
}
- A) Extract Method
- B) Inline Variable — шаардлагагүй түр хувьсагч устгах
- C) Rename
- D) Move Method
Зөв хариулт: B
Тайлбар:
primary,secondary,tertiaryхувьсагчид нь зөвхөн нэг удаа ашиглагдаж байна →return basePrice() + secondaryPrice() + tertiaryPrice();→ Inline Variable.
Тест 87
"Decompose Conditional" refactoring юу хийдэг вэ?
- A) Нөхцөл устгах
- B) Нарийн нөхцөлийн expression-ийг тусдаа нэрлэсэн методуудад гаргах
- C) Нөхцөл нэмэх
- D) switch-ийг if-ээр солих
Зөв хариулт: B
Тайлбар:
if (date.before(SUMMER_START) || date.after(SUMMER_END))→if (isWinter(date)). Нарийн нөхцөлийг нэрлэсэн методод гаргах → Ойлгомжтой.
Тест 88
"Consolidate Conditional Expression" юу хийдэг вэ?
- A) Нөхцөл устгах
- B) Ижил үр дүн буцаадаг олон нөхцөлийг нэгтгэх
- C) Нөхцөл нэмэх
- D) Нөхцөл хуваах
Зөв хариулт: B
Тайлбар:
if (a) return 0; if (b) return 0; if (c) return 0;→if (a || b || c) return 0;→if (isIneligible()) return 0;. Нэгтгэж, Extract Method хийнэ.
Тест 89
Дараах кодонд ямар code smell байна?
class Manager extends Employee {
@Override double getSalary() { return super.getSalary() + bonus; }
// getVacationDays(), getTitle() зэргийг ХЭРЭГЛЭХГҮЙ
}
- A) Duplicate Code
- B) Refused Bequest — эх классаас олон зүйл "өвлөж" авсан ч ашигладаггүй
- C) Dead Code
- D) Long Method
Зөв хариулт: B
Тайлбар: Manager нь Employee-ийн олон методыг хэрэглэхгүй → Refused Bequest. Удамшлын бүтцийг дахин авч үзэх, эсвэл composition ашиглах.
Тест 90
Code smell олоход ямар хэрэгсэл тусалдаг вэ?
- A) Зөвхөн нүдээр хайх
- B) SonarQube, PMD, CheckStyle, IntelliJ Inspections зэрэг статик анализын хэрэгслүүд
- C) Зөвхөн тест
- D) Зөвхөн Code Review
Зөв хариулт: B
Тайлбар: SonarQube, PMD, CheckStyle зэрэг нь кодыг автоматаар шалгаж, code smell, давхардал, нарийн нөхцөл зэргийг олдог. IDE inspections ч мөн.
Тест 91
Refactoring хийснээр кодын ямар чанарын шинж САЙЖРАХ вэ?
- A) Зөвхөн хурд
- B) Уншигдах чанар, засварлах чанар, уян хатан чанар
- C) Зөвхөн аюулгүй байдал
- D) Зөвхөн хэмжээ
Зөв хариулт: B
Тайлбар: Readability (уншигдах), Maintainability (засварлах), Flexibility (уян хатан). Refactoring нь кодын ЧАНАРЫН шинжүүдийг сайжруулна.
Тест 92
"Replace Inheritance with Delegation" юу хийдэг вэ?
- A) Удамшил нэмэх
- B) Буруу удамшлыг composition/delegation-оор солих
- C) Делегат устгах
- D) Интерфейс нэмэх
Зөв хариулт: B
Тайлбар:
class Stack extends ArrayList→class Stack { private List list; }. "Is-a" биш "Has-a" харилцаа бол composition ашиглах нь зөв.
Тест 93
Refactoring хийх үед "одоо байгаа тестүүд бүгд pass" байх ёстой. Яагаад?
- A) Менежер шаарддаг
- B) Гадаад зан төлөв өөрчлөгдөөгүй гэдгийг баталгаажуулна
- C) Код хурдасна
- D) Шинэ функц нэмэгдсэн гэсэн үг
Зөв хариулт: B
Тайлбар: Refactoring = Гадаад зан төлөв ИЖИЛ. Тест pass = Зан төлөв өөрчлөгдөөгүй = Refactoring ЗӨӨВ хийгдсэн.
Тест 94
Дараах кодонд ямар refactoring хийх вэ?
double price = order.getBasePrice() - order.getDiscount() + order.getTax();
- A) Dead Code устгах
- B)
order.getTotalPrice()→ Move Method (Order класст зөөх) - C) Rename
- D) Inline Variable
Зөв хариулт: B
Тайлбар: Feature Envy — гаднаас Order-ийн олон утгыг ашиглаж байна.
getTotalPrice()методыг Order класс дотор бичих нь зөв.
Тест 95
"Parameterize Method" refactoring юу хийдэг вэ?
- A) Параметр устгах
- B) Ижил логиктой олон методыг нэг параметрчилсэн метод болгон нэгтгэх
- C) Параметрын төрөл солих
- D) Шинэ метод үүсгэх
Зөв хариулт: B
Тайлбар:
fivePercentRaise(),tenPercentRaise()→raise(double percentage). Ижил логик, өөр утгатай методуудыг нэг параметрчилсэн метод болгоно.
Тест 96
Clean Code-ийн "Functions should do one thing" зарчим refactoring-тэй хэрхэн холбогдох вэ?
- A) Холбогдохгүй
- B) Long Method smell → Extract Method хийж, метод бүрийг нэг зүйл хийхээр хуваах
- C) Зөвхөн тестэд
- D) Зөвхөн design pattern
Зөв хариулт: B
Тайлбар: Robert C. Martin: "Функц нэг зүйл хийх ёстой." Long Method → Extract Method = Нэг метод нэг үүрэг. SRP зарчим метод түвшинд.
Тест 97
Дараах аль нь refactoring хийх САЙН цаг вэ?
- A) Шинэ функц нэмэхийн ӨМНӨ
- B) Deadline дуусах өдөр
- C) Production дээр шууд
- D) Тест бичээгүй үед
Зөв хариулт: A
Тайлбар: Шинэ функц нэмэхийн өмнө кодыг refactor хийвэл → Шинэ зүйл оруулахад хялбар. "Preparatory refactoring" гэж нэрлэдэг.
Тест 98
Дараах аль нь refactoring-ийн эрсдэл вэ?
- A) Код ойлгомжтой болно
- B) Тест хангалтгүй бол гадаад зан төлөв санамсаргүйгээр өөрчлөгдөж болно
- C) Код хурдасна
- D) Шинэ функц нэмэгдэнэ
Зөв хариулт: B
Тайлбар: Тестгүйгээр refactoring = Эрсдэлтэй. Гадаад зан төлөв өөрчлөгдсөнийг мэдэхгүй → Хэрэглэгчид нөлөөлнө. Тест = Аюулгүй байдлын тор.
Тест 99
"Refactoring: Improving the Design of Existing Code" номны 2 дахь хэвлэл хэдэн онд гарсан вэ?
- A) 1999
- B) 2008
- C) 2018
- D) 2023
Зөв хариулт: C
Тайлбар: 1-р хэвлэл: 1999 он (Java жишээтэй). 2-р хэвлэл: 2018 он (JavaScript жишээтэй). Martin Fowler зохиогч.
Тест 100
Refactoring яагаад програм хангамжийн бүтээлтэд чухал вэ?
- A) Зөвхөн IDE шаарддаг
- B) Кодын чанарыг байнга сайжруулж, техникийн өрийг бууруулж, багийн бүтээмжийг нэмэгдүүлдэг
- C) Шинэ функц нэмэхэд л хэрэгтэй
- D) Компайлер шаарддаг
Зөв хариулт: B
Тайлбар: Refactoring нь кодын ЧАНАР-ыг байнга сайжруулж, ТЕХНИКИЙН ӨР-ийг бууруулж, кодыг ОЙЛГОМЖТОЙ, ЗАСВАР ХЯЛБАР болгож, багийн БҮТЭЭМЖ-ийг нэмэгдүүлдэг. Мэргэжлийн хөгжүүлэгчийн зайлшгүй ур чадвар.
📚 Ашигласан эх сурвалжууд:
- Martin Fowler — Refactoring: Improving the Design of Existing Code (1st ed. 1999, 2nd ed. 2018)
- Robert C. Martin — Clean Code: A Handbook of Agile Software Craftsmanship
- Joshua Kerievsky — Refactoring to Patterns
- refactoring.guru — Refactoring & Code Smell catalog
- Kent Beck — Smalltalk Best Practice Patterns
- IEEE SWEBOK — Software Construction Knowledge Area
- IntelliJ IDEA Documentation — Refactoring Features