Principiul Inversării Dependenței în SWIFT


Principiul Inversării Dependenței sau Dependency Inversion Principle afirmă faptul că modulele de pe nivelul ierarhic superior trebuie să fie decuplate de cele de pe nivelurile ierarhice inferioare. Această decuplare între modulele superioare și cele de nivel inferior se va realiza prin introducerea unui nivel tampon de abstractizare între clasele care formează nivelul ierarhic superior și cele care formează nivelurile ierarhice inferioare.
În plus principiul consideră că abstractizarea nu trebuie să depindă de detalii, ci detaliile trebuie sa depindă de abstractizare. Acest principiu este foarte important pentru reutilizarea componentelor software. De asemenea, aplicarea corectă a acestui principiu face ca întreținerea codului să fie mult mai ușor de realizat.

Imediat când citim o astfel de definiție, gândul ne duce la protocoale și la ceea ce înseamnă abstractizarea în SWIFT.

Să intrăm mai mult în detalii.

Avem cazul concret în cadrul aplicației pe care-o dezvoltăm împreună cu elevii din grupul nostru Apple App@DMT.
Construcției ViewController-ul dedicat afisării detaliilor pentru ofertă -> aici

Avem clasele OfferDetailsTableViewCell1 și OfferDetailsTableViewCell2
import UIKit

class OfferDetailsTableViewCell2: UITableViewCell {

    static let ReuseIdentifier = String(describing: OfferDetailsTableViewCell2.self)
    static let NibName = String(describing: OfferDetailsTableViewCell2.self)
    @IBOutlet weak var descriptionLabel: UILabel!
  
}

și

class OfferDetailsTableViewCell1: UITableViewCell {

    static let ReuseIdentifier = String(describing: OfferDetailsTableViewCell1.self)
    static let NibName = String(describing: OfferDetailsTableViewCell1.self)
    @IBOutlet weak var headerLabel: UILabel!
    @IBOutlet weak var dateLabel: UILabel!
    @IBOutlet weak var priceLabel: UILabel!
}

clase acoperitoare pentru doua XIB-uri de UITableViewCell.

Ceea ce m-a făcut să scriu acest articol este modul în care este definit delegatul tableView(tableView:, cellForRowAt indexPath:)

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        switch cellDataArray[indexPath.row].cell {
        case 1:
            let  cell = tableView.dequeueReusableCell(withIdentifier: OfferDetailsTableViewCell1.ReuseIdentifier) as! OfferDetailsTableViewCell1
            cell.headerLabel.text = cellDataArray[indexPath.row].text
            cell.dateLabel.text = "BUBU"
            cell.priceLabel.text = "10 RON"
            return cell
            
        case 2:
             let cell = tableView.dequeueReusableCell(withIdentifier: OfferDetailsTableViewCell2.ReuseIdentifier) as! OfferDetailsTableViewCell2
            cell.descriptionLabel.text = cellDataArray[indexPath.row].text
             return cell

        default:
             let cell = tableView.dequeueReusableCell(withIdentifier: OfferDetailsTableViewCell1.ReuseIdentifier) as! OfferDetailsTableViewCell1
             cell.headerLabel.text = cellDataArray[indexPath.row].text
             cell.dateLabel.text = "BUBU"
             cell.priceLabel.text = "10 RON"
            return cell
        }
    
    }

Vedem că în fiecare case din switch se vor instanția cell-uri și, de asemenea, se vor returna aceste cell-uri.
Orice modificare facută în clasa inferioară, OfferDetailsTableViewCell2 sau OfferDetailsTableViewCell1, trebuie de asemenea sa se opereze în clasele de nivel superior, în cazul nostru OfferDtailsViewController care implementează UITableViewDelegate și UITableViewDataSource.

Cum rezolvăm această cuplare?

1. Introducem un protocol în care declarăm o functie config(withData:)

protocol OffersTableViewCellProtocol {
    func config(withData:Any)
}

2. Acest protocol este adoptat de fiecare dintre clasele OfferDetailsTableViewCell1 și OfferDetailsTableViewCell2

class OfferDetailsTableViewCell1: UITableViewCell, OffersTableViewCellProtocol {

    func config(withData: Any) {
        let data = withData as! cellData
        self.headerLabel.text = data.text
        self.dateLabel.text = "BUBU"
        self.priceLabel.text = "10 RON"
    }
    
    static let ReuseIdentifier = String(describing: OfferDetailsTableViewCell1.self)
    static let NibName = String(describing: OfferDetailsTableViewCell1.self)
    
    @IBOutlet weak var headerLabel: UILabel!
    @IBOutlet weak var dateLabel: UILabel!
    @IBOutlet weak var priceLabel: UILabel!
}

și

class OfferDetailsTableViewCell2: UITableViewCell, OffersTableViewCellProtocol {
    
    func config(withData: Any) {
        let data = withData as! cellData
        self.descriptionLabel.text = data.text
    }

    static let ReuseIdentifier = String(describing: OfferDetailsTableViewCell2.self)
    static let NibName = String(describing: OfferDetailsTableViewCell2.self)
    
    @IBOutlet weak var descriptionLabel: UILabel!
    
}

3. Rescriem delegatul tableView(tableView:, cellForRowAt indexPath:)

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        var cell:OffersTableViewCellProtocol? = nil
        
        switch cellDataArray[indexPath.row].cell {
        case 1:
                cell = tableView.dequeueReusableCell(withIdentifier: OfferDetailsTableViewCell1.ReuseIdentifier) as? OffersTableViewCellProtocol
                cell?.config(withData: cellDataArray[indexPath.row])
            
        case 2:
                cell = tableView.dequeueReusableCell(withIdentifier: OfferDetailsTableViewCell2.ReuseIdentifier) as? OffersTableViewCellProtocol
                cell?.config(withData: cellDataArray[indexPath.row])

        default:
            cell = tableView.dequeueReusableCell(withIdentifier: OfferDetailsTableViewCell1.ReuseIdentifier) as? OffersTableViewCellProtocol
                cell?.config(withData: cellDataArray[indexPath.row])
        }

        return cell as! UITableViewCell
    }

Prin intermediul protocolului OffersTableViewCellProtocol am reușit să punem în aplicare principiul inversării dependenței astfel încăt decuplarea dintre cele nivele să fie funcțională.

Share Button

Stefan

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.