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ă.