Ingineria Sistemelor de Programe

Documentatie suplimentara, ISP, Seria B si SM

View on GitHub

Principiile de baza OOP

Cuprins

  1. Principiul Încapsulării
  2. Principiul Moștenirii
  3. Principiul Polimorfismului
  4. Principiul Abstractizării

Principiul Încapsulării

Încapsularea este unul dintre principiile fundamentale ale OOP care:

Implementarea încapsulării:

  1. Declararea atributelor ca private
  2. Furnizarea metodelor publice de acces (getteri) și modificare (setteri)
public class ContBancar {
    // Atribute private (ascunse față de exterior)
    private String numarCont;
    private double sold;
    private String proprietar;

    // Constructor
    public ContBancar(String numarCont, String proprietar) {
        this.numarCont = numarCont;
        this.proprietar = proprietar;
        this.sold = 0.0;
    }

    // Metode Getter - permit citirea atributelor
    public String getNumarCont() {
        return numarCont;
    }

    public String getProprietar() {
        return proprietar;
    }

    public double getSold() {
        return sold;
    }

    // Metode Setter - permit modificarea controlată a atributelor
    public void setProprietar(String proprietar) {
        this.proprietar = proprietar;
    }

    // Nu oferim setter pentru numarCont - nu dorim să permitem modificarea

    // Metode de business - implementează logica de modificare a soldului
    public void depunere(double suma) {
        if (suma > 0) {
            sold += suma;
        }
    }

    public boolean retragere(double suma) {
        if (suma > 0 && suma <= sold) {
            sold -= suma;
            return true;
        }
        return false;
    }
}

Avantajele încapsulării:

Principiul Moștenirii

Moștenirea permite crearea de noi clase (clase derivate) bazate pe clase existente (clase de bază), preluând și extinzând funcționalitățile acestora.

Implementarea moștenirii:

  1. Utilizarea cuvântului cheie extends în Java (sau echivalent în alte limbaje)
  2. Crearea de subclase specializate
public class ContEconomii extends ContBancar {
    // Atribut adițional
    private double rataDobanzii;
    
    // Constructor
    public ContEconomii(String numarCont, String proprietar, double rataDobanzii) {
        super(numarCont, proprietar); // Apel constructor clasa părinte
        this.rataDobanzii = rataDobanzii;
    }
    
    // Getter și Setter pentru noul atribut
    public double getRataDobanzii() {
        return rataDobanzii;
    }
    
    public void setRataDobanzii(double rataDobanzii) {
        this.rataDobanzii = rataDobanzii;
    }
    
    // Metodă specifică pentru calculul dobânzii
    public void aplicaDobanda() {
        double dobanda = getSold() * rataDobanzii / 100;
        depunere(dobanda); // Folosim metoda moștenită
    }
}

Avantajele moștenirii:

Principiul Polimorfismului

Polimorfismul permite obiectelor de diferite tipuri să răspundă la aceeași interfață în moduri specifice fiecărui tip.

Polimorfism static (suprascrierea metodelor)

public class ContCurent extends ContBancar {
    private double limitaDescoperire;
    
    public ContCurent(String numarCont, String proprietar, double limitaDescoperire) {
        super(numarCont, proprietar);
        this.limitaDescoperire = limitaDescoperire;
    }
    
    // Suprascrierea metodei din clasa părinte
    @Override
    public boolean retragere(double suma) {
        if (suma > 0 && suma <= (getSold() + limitaDescoperire)) {
            // Putem retrage până la limita descoperire
            if (super.retragere(suma)) {
                return true;
            } else {
                double soldNou = getSold() - suma;
                // Logică diferită pentru cont curent
                return true;
            }
        }
        return false;
    }
}

Polimorfism dinamic (interfețe și clase abstracte)

public interface Tranzactionabil {
    boolean proceseazaTranzactie(double suma);
    String getIdentificator();
}

public class ContBancar implements Tranzactionabil {
    // ... implementarea clasei de bază
    
    @Override
    public boolean proceseazaTranzactie(double suma) {
        return retragere(suma);
    }
    
    @Override
    public String getIdentificator() {
        return getNumarCont();
    }
}

Avantajele polimorfismului:

Principiul Abstractizării

Abstractizarea este procesul de ascundere a detaliilor de implementare și de expunere a funcționalității esențiale. În OOP, abstractizarea înseamnă să arătăm ce face un obiect, nu cum o face.

Scopul abstractizării

Cum realizăm abstractizarea în Java?

Java oferă două mecanisme principale:

  1. Clase abstracte (abstract class)
  2. Interfețe (interface)

Exemplu cu abstract class

abstract class Animal {
    abstract void makeSound(); // metodă abstractă

    void breathe() { // metodă concretă
        System.out.println("Breathing...");
    }
}

class Dog extends Animal {
    @Override
    void makeSound() {
        System.out.println("Woof!");
    }
}

Explicație

Exemplu cu interface

interface Shape {
    double area();
}

class Circle implements Shape {
    double radius;

    Circle(double r) {
        this.radius = r;
    }

    public double area() {
        return Math.PI * radius * radius;
    }
}

Explicație

Beneficiile abstractizării

💳 Exemplu real: Sistem de Plată

Să presupunem că ai o aplicație care permite efectuarea de plăți prin mai multe metode: Card bancar, PayPal și Crypto.

1. Interfață de abstractizare

public interface PaymentMethod {
    void pay(double amount);
}

2. Implementări concrete

public class CreditCardPayment implements PaymentMethod {
    public void pay(double amount) {
        System.out.println("Paid " + amount + " via Credit Card.");
    }
}

public class PayPalPayment implements PaymentMethod {
    public void pay(double amount) {
        System.out.println("Paid " + amount + " via PayPal.");
    }
}

public class CryptoPayment implements PaymentMethod {
    public void pay(double amount) {
        System.out.println("Paid " + amount + " via Cryptocurrency.");
    }
}

3. Clasa care folosește abstractizarea

public class PaymentProcessor {
    public void processPayment(PaymentMethod method, double amount) {
        method.pay(amount);
    }
}

4. Exemplu de utilizare

public class Main {
    public static void main(String[] args) {
        PaymentProcessor processor = new PaymentProcessor();

        processor.processPayment(new CreditCardPayment(), 150.0);
        processor.processPayment(new PayPalPayment(), 75.0);
        processor.processPayment(new CryptoPayment(), 300.0);
    }
}

Ce demonstrează acest exemplu?