Ghid complet cu exemple interactive în Java
Singleton Pattern este un design pattern creațional care asigură că o clasă are o singură instanță și oferă un punct global de acces la aceasta. Exemplul tău folosește implementarea Lazy Holder, care este thread-safe și eficientă.
public class OrdersQueryServiceSingleton {
// Clasa internă statică - încărcată doar când este accesată
private static class LazyHolder {
private static final OrdersQueryService ORDERS_QUERY_SERVICE =
new OrdersQueryServiceImpl();
}
// Metodă publică pentru a obține instanța unică
public static OrdersQueryService getInstance() {
return LazyHolder.ORDERS_QUERY_SERVICE;
}
}
Inițializarea este garantată thread-safe de JVM
Instanța este creată doar când este necesară
Nu necesită sincronizare explicită
Apasă butoanele pentru a vedea cum funcționează!
Builder Pattern este un design pattern creațional care permite construirea obiectelor complexe pas cu pas. Vom explora două implementări: versiunea clasică și versiunea cu moștenire pentru ierarhii complexe de clase.
public class Order {
private final String orderId;
private final String customerName;
private final String product;
private final int quantity;
private final double price;
private final String shippingAddress;
// Constructor privat - accesibil doar prin Builder
private Order(Builder builder) {
this.orderId = builder.orderId;
this.customerName = builder.customerName;
this.product = builder.product;
this.quantity = builder.quantity;
this.price = builder.price;
this.shippingAddress = builder.shippingAddress;
}
// Clasa Builder internă
public static class Builder {
private String orderId;
private String customerName;
private String product;
private int quantity = 1;
private double price;
private String shippingAddress;
public Builder orderId(String orderId) {
this.orderId = orderId;
return this;
}
public Builder customerName(String customerName) {
this.customerName = customerName;
return this;
}
public Builder product(String product) {
this.product = product;
return this;
}
public Order build() {
// Validări
if (orderId == null || customerName == null) {
throw new IllegalStateException(
"Order ID și Customer Name sunt obligatorii!"
);
}
return new Order(this);
}
}
}
// Clasa de bază abstractă
public abstract class Product {
protected final String id;
protected final String name;
protected final double price;
protected final String description;
protected Product(Builder<?> builder) {
this.id = builder.id;
this.name = builder.name;
this.price = builder.price;
this.description = builder.description;
}
// Generic Builder base class
public abstract static class Builder<T extends Builder<T>> {
protected String id;
protected String name;
protected double price;
protected String description;
// Abstract method pentru type-safe chaining
protected abstract T self();
public T id(String id) {
this.id = id;
return self();
}
public T name(String name) {
this.name = name;
return self();
}
public T price(double price) {
this.price = price;
return self();
}
public abstract Product build();
}
}
// Produs fizic cu proprietăți specifice
class PhysicalProduct extends Product {
private final double weight;
private final boolean fragile;
private PhysicalProduct(Builder builder) {
super(builder);
this.weight = builder.weight;
this.fragile = builder.fragile;
}
public static Builder builder() {
return new Builder();
}
public static class Builder extends Product.Builder<Builder> {
private double weight;
private boolean fragile;
@Override
protected Builder self() {
return this;
}
public Builder weight(double weight) {
this.weight = weight;
return this;
}
public Builder fragile(boolean fragile) {
this.fragile = fragile;
return this;
}
@Override
public PhysicalProduct build() {
return new PhysicalProduct(this);
}
}
}
// Produs digital cu proprietăți specifice
class DigitalProduct extends Product {
private final String downloadUrl;
private final String licenseType;
private DigitalProduct(Builder builder) {
super(builder);
this.downloadUrl = builder.downloadUrl;
this.licenseType = builder.licenseType;
}
public static Builder builder() {
return new Builder();
}
public static class Builder extends Product.Builder<Builder> {
private String downloadUrl;
private String licenseType = "SINGLE_USER";
@Override
protected Builder self() {
return this;
}
public Builder downloadUrl(String url) {
this.downloadUrl = url;
return this;
}
public Builder licenseType(String type) {
this.licenseType = type;
return this;
}
@Override
public DigitalProduct build() {
return new DigitalProduct(this);
}
}
}
🎯 Rezultatele construirii vor apărea aici...
Testează diferite tipuri de builder-e pentru a vedea diferențele!
Cele 5 principii fundamentale ale programării orientate pe obiecte
O clasă ar trebui să aibă un singur motiv pentru a se schimba
Fiecare clasă ar trebui să aibă o singură responsabilitate și această responsabilitate ar trebui să fie complet încapsulată în clasă.
public class Employee {
private String name;
private double salary;
// Responsabilitate 1: Logică de business pentru angajat
public void calculateSalary() {
// calcul salariu
}
// Responsabilitate 2: Persistență în baza de date
public void saveToDatabase() {
// salvare în DB
}
// Responsabilitate 3: Generare rapoarte
public void generateReport() {
// generare PDF
}
}
// Clasa pentru logica de business
public class Employee {
private String name;
private double salary;
// Doar metode legate de angajat
public double calculateSalary() {
return salary * 1.2; // exemplu calcul
}
}
// Clasa separată pentru persistență
public class EmployeeRepository {
public void save(Employee employee) {
// logica de salvare
}
}
// Clasa separată pentru rapoarte
public class EmployeeReportGenerator {
public void generateReport(Employee employee) {
// logica de generare raport
}
}
Clasele ar trebui să fie deschise pentru extensie, dar închise pentru modificare
Ar trebui să putem extinde comportamentul unei clase fără a modifica codul sursă existent.
public class DiscountCalculator {
public double calculateDiscount(Order order, String customerType) {
if (customerType.equals("REGULAR")) {
return order.getAmount() * 0.05;
} else if (customerType.equals("SILVER")) {
return order.getAmount() * 0.10;
} else if (customerType.equals("GOLD")) {
return order.getAmount() * 0.20;
}
// Pentru fiecare tip nou, trebuie să modificăm această metodă!
return 0;
}
}
// Interfață pentru strategii de discount
public interface DiscountStrategy {
double calculateDiscount(Order order);
}
// Implementări concrete
public class RegularCustomerDiscount implements DiscountStrategy {
public double calculateDiscount(Order order) {
return order.getAmount() * 0.05;
}
}
public class SilverCustomerDiscount implements DiscountStrategy {
public double calculateDiscount(Order order) {
return order.getAmount() * 0.10;
}
}
public class PlatinumCustomerDiscount implements DiscountStrategy {
public double calculateDiscount(Order order) {
return order.getAmount() * 0.25; // Nou tip adăugat fără modificări!
}
}
// Calculator care nu necesită modificare pentru tipuri noi
public class DiscountCalculator {
public double calculateDiscount(Order order, DiscountStrategy strategy) {
return strategy.calculateDiscount(order);
}
}
Obiectele unei clase derivate trebuie să poată înlocui obiectele clasei de bază fără a afecta corectitudinea programului
Subtipurile trebuie să fie substituibile pentru tipurile lor de bază. Dacă S este un subtip al lui T, atunci obiectele de tip T pot fi înlocuite cu obiecte de tip S fără a altera proprietățile programului.
public class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
// Pătratul încalcă LSP!
public class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // Forțăm lățimea = înălțimea
}
@Override
public void setHeight(int height) {
this.width = height;
this.height = height; // Forțăm lățimea = înălțimea
}
}
// Interfață comună
public interface Shape {
int getArea();
}
// Rectangle independent
public class Rectangle implements Shape {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() {
return width * height;
}
}
// Square independent
public class Square implements Shape {
private int side;
public Square(int side) {
this.side = side;
}
public int getArea() {
return side * side;
}
}
Clienții nu ar trebui să fie forțați să depindă de interfețe pe care nu le folosesc
Este mai bine să avem mai multe interfețe specifice decât o singură interfață generală. Interfețele mari ar trebui împărțite în interfețe mai mici și mai specifice.
public interface Worker {
void work();
void eat();
void sleep();
void receiveSalary();
}
// Un robot nu mănâncă sau doarme!
public class RobotWorker implements Worker {
public void work() {
// Robot lucrează
}
public void eat() {
throw new UnsupportedOperationException("Roboții nu mănâncă!");
}
public void sleep() {
throw new UnsupportedOperationException("Roboții nu dorm!");
}
public void receiveSalary() {
// Robot primește întreținere
}
}
// Interfețe specifice
public interface Workable {
void work();
}
public interface Eatable {
void eat();
}
public interface Sleepable {
void sleep();
}
public interface Payable {
void receiveSalary();
}
// Angajat uman implementează toate interfețele necesare
public class HumanWorker implements Workable, Eatable, Sleepable, Payable {
public void work() {
System.out.println("Angajatul lucrează");
}
public void eat() {
System.out.println("Angajatul mănâncă");
}
public void sleep() {
System.out.println("Angajatul doarme");
}
public void receiveSalary() {
System.out.println("Angajatul primește salariu");
}
}
// Robot implementează doar interfețele relevante
public class RobotWorker implements Workable, Payable {
public void work() {
System.out.println("Robotul lucrează non-stop");
}
public void receiveSalary() {
System.out.println("Robotul primește întreținere");
}
}
Modulele de nivel înalt nu ar trebui să depindă de module de nivel jos. Ambele ar trebui să depindă de abstracții
Abstracțiile nu ar trebui să depindă de detalii. Detaliile ar trebui să depindă de abstracții. Acest principiu promovează loose coupling între componente.
public class EmailService {
public void sendEmail(String message) {
System.out.println("Sending email: " + message);
}
}
// Clasa de nivel înalt depinde direct de implementare
public class OrderService {
private EmailService emailService = new EmailService();
public void processOrder(Order order) {
// Procesare comandă
emailService.sendEmail("Order processed: " + order.getId());
}
}
// Abstracție pentru notificări
public interface NotificationService {
void sendNotification(String message);
}
// Implementări concrete
public class EmailNotificationService implements NotificationService {
public void sendNotification(String message) {
System.out.println("Email: " + message);
}
}
public class SMSNotificationService implements NotificationService {
public void sendNotification(String message) {
System.out.println("SMS: " + message);
}
}
public class PushNotificationService implements NotificationService {
public void sendNotification(String message) {
System.out.println("Push: " + message);
}
}
// Clasa de nivel înalt depinde de abstracție
public class OrderService {
private final NotificationService notificationService;
// Dependency injection prin constructor
public OrderService(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void processOrder(Order order) {
// Procesare comandă
notificationService.sendNotification("Order processed: " + order.getId());
}
}