SOLID Overview
5 guidelines ูุชุฌูุจ bad design. ุญุฑูู SOLID = ุงุฎุชุตุงุฑ ููู 5 principles.
- Author of Clean Code + Agile Software Craftsmanship.
- SOLID = set of guidelines to avoid bad OOP design.
- ุงูู goal: code maintainable, extensible, testable.
S โ Single Responsibility Principle (SRP)
"A class should have only one reason to change."
- ูู class ุจูู ุซู concept ูุงุญุฏ ููุญู ู responsibility ูุงุญุฏุฉ.
- ูู ููู 2 reasons ูุชุบููุฑ ุงูู classุ ูุจูู ูุงุฒู ุชูุณู ู ูู class ุงุชููู.
- ูู class ููุชู ุจู one responsibility + one reason to change.
โ Bad โ 2 responsibilities
class User {
String name;
String email;
void save() {
// DB logic
}
void sendEmail() {
// SMTP logic
}
}
โ ๏ธ 2 reasons to change: DB schema changes OR email server changes.
โ Good โ SRP
class User {
String name; email;
}
class UserRepository {
void save(User u) {...}
}
class EmailService {
void send(User u) {...}
}
โ Each class has ONE reason to change.
O โ Open-Close Principle (OCP) โญ
"Software entities should be open for extension, but closed for modification." โ ุงูุฃูู ูุฃูู ุฃุณุงุณ Strategy Pattern.
ุงูุฏุฑ ุชุถูู functionality ุฌุฏูุฏ ุจุฏูู ู ุง ุชุบููุฑ existing code.
ุฒู mixer kitchen โ ุชูุฏุฑ ุชุบูุฑ ุงูู attachments ุจุฏูู ู ุง ุชุบูุฑ ุงูู engine.
ููู ูุทุจููุ
- Use Interfaces / Abstract Classes.
- Use Design Patterns (Strategy, Decorator, etc.).
ุนุงูุฒ ArrayList ุจุณู sort() method. ุงุจุฏุฃ ุจู bubble sort:
class ArrayList<T> {
void sort() { /* bubble sort */ }
}
ุนุงูุฒ ุชุจุฏูู ูู merge sortุ ุงุญุฐู bubble ูุถู merge. ุณูู.
ุนุงูุฒ ุงูุงุชููู ู ุชุงุญููุ ุถู if statement:
void sort(int sortChoice) {
if (sortChoice == 1) { /* merge sort */ }
if (sortChoice == 2) { /* bubble sort */ }
}
"When writing a class, ensure that when you need to extend its behavior, you don't have to change the class but to extend it."
ุงูุญู: ุงุณุชุฎุฏู Abstract Classes + concrete classes ูุชูููุฐ behavior. ุฏู ุงูู Strategy Pattern โ ูุชุดููู ุชูุตููุงู ูู Lecture 12.
L โ Liskov Substitution Principle (LSP)
"Subtypes must be substitutable for their base types."
ูู S ูู subtype ู ู Tุ ูุจูู objects ู ู type T ู ู ูู ุชุชุจุฏู ุจู objects ู ู type S ุจุฏูู ู ุง ุชุฃุซุฑ ุนูู correctness.
ุงููู ุงูู subclass ู ุง ููุบูุด ุฃู ููุณุฑุด contract ุงูู superclass.
โ Violates LSP
class Rectangle {
int w, h;
void setWidth(int w) { ... }
void setHeight(int h) { ... }
}
// Square is-a Rectangle?
class Square extends Rectangle {
// w must = h always
void setWidth(int w) {
this.w = w;
this.h = w; // surprise!
}
}
โ Test if substitutable
void resize(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
assert(r.area() == 20);
}
// Passing a Square FAILS:
// setHeight changes width too
// โ area = 16, not 20
// Square is NOT substitutable for Rectangle
ูู ุนูุฏู Bird class ูููุง fly()ุ ู Penguin extends Bird โ ุจุณ ุงูุจุทุฑูู ู
ุง ุจูุทูุฑุด! โ ุจููุณุฑ ุงูู substitutability.
ุงูุญู: ุงูุตู ุงูุทููุฑ ุงูุทุงูุฑุฉ ุนู ุบูุฑ ุงูุทุงูุฑุฉ. ุงุณุชุฎุฏู
interfaces / abstractions ู
ูุงุณุจุฉ (ู
ุซูุงู Flyable ู
ููุตู).
ุนูุฏู Employee hierarchyุ ู VolunteerEmployee extends Employee. ุจุณ ุงูู VolunteerEmployee ู
ููุด salary/payment โ ุงูู calcPay() ุจุชุจูู ู
ุดููุฉ.
Employee base class ููุถุทุฑ ูุนู
ู explicit reference ููุงุญุฏ ู
ู ุงูู derivatives (ูููุญุต: ูู ุฏู VolunteerEmployeeุ). ุฏู violation ุตุฑูุญ ููู LSP:
VolunteerEmployeeู ุด substitutable ููEmployee.- ุงูู users ุจุชูุน
Employeeุจูุชุฃุซุฑูุง ุจู ุฌุฑุฏ ูุฌูุฏ ุงููVolunteerEmployee.
- ุงูู Volunteers ู ุด employees ุฃุตูุงู.
VolunteerEmployeeู ุง ููุฑูุซุด ู ูEmployee.- ู
ุง ูุชุจุนุชุด ูู functions ุจุชุญุชุงุฌ
calcPay().
I โ Interface Segregation Principle (ISP)
"Clients should not be forced to depend upon interfaces they do not use."
ูุณูู ุงูู "fat" interfaces ูู smaller, more specific ones.
ุงูู "Fat" interfaces ุจุชุคุฏู ูู "polluted" classes โ ู
ุฌุจุฑูู implement methods ู
ุง ูุญุชุงุฌููุงุด (ุบุงูุจุงู ุจูุฑู
ูุง NotImplementedException).
โ Fat interface
interface Worker {
void work();
void eat();
void sleep();
}
class Robot implements Worker {
void work() {...}
void eat() {
throw new
NotImplementedException();
}
// Robots don't eat/sleep!
}
โ Segregated interfaces
interface Workable {
void work();
}
interface Feedable {
void eat();
}
class Robot implements
Workable {
void work() {...}
}
class Human implements
Workable, Feedable {
void work() {...}
void eat() {...}
}
ุนูุฏู IDoor { lock(); unlock(); isOpen(); }. ุฌู ุนุงูุฒ SecurityDoor ุชุทููู alarm ูู ูุถูุช ู
ูุชูุญุฉ โ ุถูุช timeOut() ููู IDoor.
timeOut() ููู IDoor ุจุชูููุซู (polluted interface) ูุจุชุฌุจุฑ ูู door ุชุนู
ู dummy implementation.โ Segregated
interface IDoor {
void lock();
void unlock();
bool isOpen();
}
interface ITimerClient {
void timeOut();
}
class SecurityDoor implements
IDoor, ITimerClient {
void timeOut() {...}
}
๐ Vehicle Example
// โ Vehicle{ drive(); fly(); }
// Car.fly() โ OOPS
// Drone.drive() โ OOPS
// โ
Split them:
interface Drivable { void drive(); }
interface Flyable { void fly(); }
class Car implements Drivable
class Airplane implements Drivable, Flyable
class Drone implements Flyable
- Many client-specific interfaces > one general-purpose interface.
- Fat interface = Fat class โ ุญุชู ุงูุชุบููุฑ ูู method ู ุด ู ุณุชุฎุฏู ุฉ ู ู ูู ูุฃุซุฑ ุนูู ุงูู users.
- ISP ู ุด ู ุนูุงู interface ูุงุญุฏ ููู class โ ุงูู ุนูู: group clients by type (ูู ุบููุฑุช ูู ClientA ู ุง ูุฃุซุฑุด ุนูู ClientB/ClientC).
- ู ุซุงู ูุงูุนู: ASP.NET Membership Provider ุฃุฌุจุฑ ุงูู custom provider ุนูู implement 27 method/property โ fat interface ููุงุณููู.
D โ Dependency Inversion Principle (DIP) โญ
"High-level modules should not depend on low-level modules. Both should depend on abstractions."
- Abstractions should not depend on details.
- Details should depend on abstractions.
ูู ุงูู traditional design: high-level modules ุจุชุนุชู ุฏ ุนูู low-level. ุฃู ุชุบููุฑ ูู low-level ุจูุฎุฑุจ high-level.
ูู ุงูู DIP: ุงูุงุชููู ุจูุนุชู ุฏูุง ุนูู interface (abstraction).
โ Direct dependency
class MySQLDatabase {
void save(String data) {...}
}
class UserService {
private MySQLDatabase db;
UserService() {
// hard-coded dependency
db = new MySQLDatabase();
}
}
โ ๏ธ UserService can't switch to Postgres without code change.
โ Depend on abstraction
interface Database {
void save(String data);
}
class MySQLDatabase
implements Database {...}
class PostgresDatabase
implements Database {...}
class UserService {
private Database db;
UserService(Database db) {
this.db = db; // injected
}
}
โ Easy swap. Easy testing (inject mock).
- ููู DIPุ ุงูู concrete classes ุจุชุชุบูุฑ ูุชูุฑ (volatile)ุ ุงูู abstractions ุจุชุชุบูุฑ ุฃูู. ุงูู abstractions = "hinge points" (ููุงุท ุงุฑุชูุงุฒ ุซุงุจุชุฉ). ูุจูุฏุนู ุงูู Open/Close Principle.
- ุขู
ู ุฅูู ุชุนุชู
ุฏ ุนูู concrete classes ู
ุณุชูุฑุฉ (stable) ุฒู
StringูVectorโ ู ุด ูู concrete class ู ู ููุนุฉ. ุชุฌููุจ ุงูู volatile concrete classes ุจุณ. - UML check: ุงูู dependency arrows ุงูู ูุฑูุถ ุชุดุงูุฑ ุนูู interfaces / abstract classes โ ู ุด ุนูู concrete classes.
Steve McConnell โ Views on Design
ู ู ูุชุงุจ "Code Complete" โ Chapter 5. ุฌููุฑ ุงูู design = managing complexity.
ุงูู design ุจููุน ุจูู ุงูู requirements ูุงูู code โ ู ุฑุญูุชูู:
- High-level design โ ุงูู system components ูุงูู connections ุจูููู .
- Low-level design โ ุงูู objects ยท classes ยท algorithms ยท variables.
- ุงูุญุฏ ุจูู ุงูุงุชููู blurry (ู ุด ูุงุถุญ ุชู ุงู ุงู).
7 Characteristics of Good Design
Minimal Complexity
Divide & conquer ยท use abstractions ยท reduce coupling ยท increase cohesion ยท remove dead code ยท avoid clever tricks ยท comment ยท document.
Loose Coupling
ูููู ุงูู connections ุจูู ุฃุฌุฒุงุก ุงูุจุฑูุงู ุฌ. ุจูุจุณูุท ุงูู integration / testing / maintenance.
Strong Cohesion
ุงูู routines ุจุชุฏุนู ุงูู purpose ุงูู ุฑูุฒู ููู module. ุงููุฏู: maximize cohesion.
Extensibility
ุชุทููุฑ ุงููุธุงู ู ู ุบูุฑ ู ุง ุชุฎุฑูุจ structure-ู. ุงูุชุบููุฑุงุช ุงูู ุชููุนุฉ ุชุณุจุจ ุฃูู trauma.
Reusability
ุฅุนุงุฏุฉ ุงุณุชุฎุฏุงู ุฃุฌุฒุงุก ูู systems ุชุงููุฉ.
Maintainability
ุฃุณูู ูู ุฅุตูุงุญ ุงูู bugs ยท ุงูุชุญุณูู ยท ุฅุถุงูุฉ features.
Leanness
ู ููุด ุฃุฌุฒุงุก ุฒูุงุฏุฉ. Voltaire: "ุงููุชุงุจ ูุฎูุต ูู ุง ู ููุด ุญุงุฌุฉ ุชุงููุฉ ู ู ูู ุชุชุดุงู". ุงูููุฏ ุงูุฒูุงุฏุฉ ุจูุฒููุฏ effort ุงูู dev/review/testing/maintenance.
Practice Quiz โ Lecture 9
8 ุฃุณุฆูุฉ ุนูู SOLID + McConnell. Q3 ูู ุงู ุชุญุงูุงุช Winter 2023 ุจู 24 marks ุนูู DIP + Adapter + Observer.
sort() method uses an if/else chain to switch between bubble, merge, and quick sort. To add a new sort algorithm requires editing this chain. Which principle is violated?Square class extends a Rectangle class. Setting width on Square also changes height, breaking caller assumptions. Which principle is violated?Worker interface has methods work(), eat(), sleep(). A Robot class implementing it must throw NotImplementedException for eat and sleep. Which principle is violated?Workable, Feedable, Sleepable.
class Service { MySQLDb db = new MySQLDb(); }class Service { Database db; Service(Database d) { this.db = d; }}class Service extends Database { }class Service { static MySQLDb db = new MySQLDb(); }MySQLDb. Violation. ยท
C โ inheritanceุ ู
ุด dependency.
Cheat Sheet
SOLID + McConnell ูู ุตูุญุฉ ูุงุญุฏุฉ.
๐ต SOLID
๐ McConnell โ 7 Good Design Traits
๐ Quick Identifiers
๐จโ๐ผ Authors
Rapid Revision
Flashcards ยท Mistakes ยท Doctor favorites.
๐จ Common Mistakes
โญ What Dr. El-Ramly Loves
- Q3 (15-24 marks) โ Apply Strategy + DIP + Adapter ูู ููุฏ ูุฏูู. ุงูุฑุฃ Lec 12 ูู ุงู.
- OCP scenario โ ูุฏู ููุฏ ููู if/else ููุทูุจ identify ุงูู violation.
- DIP code โ Winter 2025 Q1.09 โ ุฃู ููุฏ implement DIPุ
- Match principle to scenario โ squarerectangle = LSP, fat interface = ISP, ุฅูุฎ.