Swift dilinde soyut bir sınıf yaratmanın bir yolu var mı, yoksa bu Objective-C gibi bir sınırlama mı? Java'nın soyut bir sınıf olarak tanımladığı şeyle karşılaştırılabilir soyut bir sınıf oluşturmak istiyorum.
Swift dilinde soyut bir sınıf yaratmanın bir yolu var mı, yoksa bu Objective-C gibi bir sınırlama mı? Java'nın soyut bir sınıf olarak tanımladığı şeyle karşılaştırılabilir soyut bir sınıf oluşturmak istiyorum.
Yanıtlar:
Swift'te soyut sınıflar yoktur (tıpkı Objective-C gibi). En iyi seçeneğiniz, Java Arayüzü gibi bir Protokol kullanmak olacaktır .
Swift 2.0 ile, protokol uzantılarını kullanarak yöntem uygulamaları ve hesaplanan özellik uygulamaları ekleyebilirsiniz. Tek kısıtlamanız üye değişkenleri veya sabitleri sağlayamamanız ve dinamik bir gönderme olmamasıdır .
Bu tekniğin bir örneği:
protocol Employee {
var annualSalary: Int {get}
}
extension Employee {
var biweeklySalary: Int {
return self.annualSalary / 26
}
func logSalary() {
print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
}
}
struct SoftwareEngineer: Employee {
var annualSalary: Int
func logSalary() {
print("overridden")
}
}
let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly
Bunun, yapılar için bile "soyut sınıf" gibi özellikler sunduğuna dikkat edin, ancak sınıflar da aynı protokolü uygulayabilir.
Ayrıca, Çalışan protokolünü uygulayan her sınıfın veya yapının tekrar yıllıkSalary özelliğini bildirmesi gerekeceğine dikkat edin.
En önemlisi, dinamik bir gönderim olmadığına dikkat edin . Ne zaman logSalary
bir şekilde depolanan örnek olarak adlandırılır SoftwareEngineer
bu yöntemin geçersiz kılınan sürümünü çağırır. Ne zaman logSalary
bir karşı döküldükten sonra örneğinde denir Employee
, bu örnek aslında bir halde değil dinamik geçersiz kılınan versiyona göndermez (orijinal uygulanmasını çağırır Software Engineer
.
Daha fazla bilgi için, bu özellikle ilgili harika WWDC videosuna bakın: Swift'te Değer Türleriyle Daha İyi Uygulamalar Oluşturma
protocol Animal { var property : Int { get set } }
. Mülkün bir ayarlayıcıya sahip olmasını istemiyorsanız seti de bırakabilirsiniz
func logSalary()
Çalışan protokolü bildirimine eklerseniz , overridden
her iki çağrı için de örnek yazdırılır logSalary()
. Bu Swift 3.1'de. Böylece polimorfizmden faydalanırsınız. Her iki durumda da doğru yöntem çağrılır.
Bu cevabın Swift 2.0 ve üzerini hedeflediğini unutmayın
Protokoller ve protokol uzantıları ile aynı davranışı elde edebilirsiniz.
İlk olarak, ona uyan tüm tiplerde uygulanması gereken tüm yöntemler için arayüz görevi gören bir protokol yazarsınız.
protocol Drivable {
var speed: Float { get set }
}
Ardından, ona uyan tüm türlere varsayılan davranış ekleyebilirsiniz
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Artık uygulayarak yeni türler oluşturabilirsiniz Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Temel olarak şunu elde edersiniz:
Drivable
uygulanmasını garanti eden zaman denetimlerini derleyinspeed
Drivable
( accelerate
) İle uyumlu tüm türler için varsayılan davranışı uygulayabilirsinizDrivable
sadece bir protokol olduğu için somutlaştırılmaması garanti edilirBu model aslında çok daha fazla özellik gibi davranır, yani birden çok protokole uyup herhangi birisinin varsayılan uygulamalarını gerçekleştirebilirsiniz, oysa soyut bir süper sınıfla basit bir sınıf hiyerarşisiyle sınırlısınız.
UICollectionViewDatasource
. Tüm boilerplate kaldırmak ve ayrı protokol / uzantısı içine kapsüllemek ve daha sonra birden çok sınıf tarafından yeniden kullanmak istiyorum. Aslında, şablon deseni burada mükemmel olurdu, ama ...
Bu Java abstract
veya C # 's en yakın olduğunu düşünüyorum abstract
:
class AbstractClass {
private init() {
}
}
private
Değiştiricilerin çalışması için bu sınıfı ayrı bir Swift dosyasında tanımlamanız gerektiğini unutmayın.
EDIT: Yine de, bu kod soyut bir yöntem bildirmek için izin vermez ve böylece uygulanmasını zorlar.
En basit yol, fatalError("Not Implemented")
protokol uzantısındaki soyut yönteme (değişken değil) bir çağrı kullanmaktır .
protocol MyInterface {
func myMethod() -> String
}
extension MyInterface {
func myMethod() -> String {
fatalError("Not Implemented")
}
}
class MyConcreteClass: MyInterface {
func myMethod() -> String {
return "The output"
}
}
MyConcreteClass().myMethod()
(MyConcreteClass() as MyInterface).myMethod()
ama işe yarıyor ! Anahtar myMethod
protokol bildirgesine dahil etmektir; aksi takdirde çağrı kilitlenir.
Birkaç hafta uğraştıktan sonra nihayet bir Java / PHP soyut sınıfını Swift'e nasıl çevireceğimizi anladım:
public class AbstractClass: NSObject {
internal override init(){}
public func getFoodToEat()->String
{
if(self._iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
internal func _iAmHungry()->Bool
{
fatalError(__FUNCTION__ + "Must be overridden");
return false;
}
}
public class ConcreteClass: AbstractClass, IConcreteClass {
private var _hungry: Bool = false;
public override init() {
super.init();
}
public func starve()->Void
{
self._hungry = true;
}
public override func _iAmHungry()->Bool
{
return self._hungry;
}
}
public protocol IConcreteClass
{
func _iAmHungry()->Bool;
}
class ConcreteClassTest: XCTestCase {
func testExample() {
var concreteClass: ConcreteClass = ConcreteClass();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.starve();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
Ancak Apple'ın soyut sınıfları uygulamadığını düşünüyorum çünkü genellikle delege + protokol modelini kullanıyor. Örneğin, yukarıdaki aynı model şu şekilde daha iyi yapılabilir:
import UIKit
public class GoldenSpoonChild
{
private var delegate: IStomach!;
internal init(){}
internal func setup(delegate: IStomach)
{
self.delegate = delegate;
}
public func getFoodToEat()->String
{
if(self.delegate.iAmHungry())
{
return self._myFavoriteFood();
}else{
return "";
}
}
private func _myFavoriteFood()->String
{
return "Sandwich";
}
}
public class Mother: GoldenSpoonChild, IStomach
{
private var _hungry: Bool = false;
public override init()
{
super.init();
super.setup(self);
}
public func makeFamilyHungry()->Void
{
self._hungry = true;
}
public func iAmHungry()->Bool
{
return self._hungry;
}
}
protocol IStomach
{
func iAmHungry()->Bool;
}
class DelegateTest: XCTestCase {
func testGetFood() {
var concreteClass: Mother = Mother();
XCTAssertEqual("", concreteClass.getFoodToEat());
concreteClass.makeFamilyHungry();
XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
}
}
UWableAppConler gibi UITableViewController bazı yöntemleri yaygınlaştırmak istedim çünkü bu tür desen gerekli. Bu yardımcı oldu mu?
Protokolleri kullanarak soyut sınıfları simüle etmenin bir yolu vardır. Bu bir örnektir:
protocol MyProtocol {
func doIt()
}
class BaseClass {
weak var myDelegate: MyProtocol?
init() {
...
}
func myFunc() {
...
self.myDelegate?.doIt()
...
}
}
class ChildClass: BaseClass, MyProtocol {
override init(){
super.init()
self.myDelegate = self
}
func doIt() {
// Custom implementation
}
}
Soyut sınıfı nasıl uygulayabileceğinizin bir yolu da başlatıcıyı engellemektir. Bu şekilde yaptım:
class Element:CALayer { // IT'S ABSTRACT CLASS
override init(){
super.init()
if self.dynamicType === Element.self {
fatalError("Element is abstract class, do not try to create instance of this class")
}
}
}
Weather
Soyut bir sınıf yapmaya çalışıyordum , ama aynı init
yöntemleri tekrar tekrar yazmak zorunda kaldığım için protokolleri kullanmak ideal değildi . Protokolün genişletilmesi ve bir init
yöntem yazmanın sorunları vardı, özellikle de NSObject
uyum sağladığımdan beri NSCoding
.
Bu yüzden NSCoding
uygunluk için bunu buldum :
required init?(coder aDecoder: NSCoder) {
guard type(of: self) != Weather.self else {
fatalError("<Weather> This is an abstract class. Use a subclass of `Weather`.")
}
// Initialize...
}
Gelince init
:
fileprivate init(param: Any...) {
// Initialize
}
Temel sınıfın soyut özelliklerine ve yöntemlerine yönelik tüm başvuruları, temel sınıf için Öz sınırlama olan protokol uzantısı uygulamasına taşıyın. Base sınıfının tüm yöntemlerine ve özelliklerine erişeceksiniz. Ayrıca derleyici sınıfları için protokolde soyut yöntem ve özelliklerin uygulanmasını kontrol
protocol Commom:class{
var tableView:UITableView {get};
func update();
}
class Base{
var total:Int = 0;
}
extension Common where Self:Base{
func update(){
total += 1;
tableView.reloadData();
}
}
class Derived:Base,Common{
var tableView:UITableView{
return owner.tableView;
}
}
Dinamik gönderim sınırlaması olmadan, böyle bir şey yapabilirsiniz:
import Foundation
protocol foo {
static var instance: foo? { get }
func prt()
}
extension foo {
func prt() {
if Thread.callStackSymbols.count > 30 {
print("super")
} else {
Self.instance?.prt()
}
}
}
class foo1 : foo {
static var instance : foo? = nil
init() {
foo1.instance = self
}
func prt() {
print("foo1")
}
}
class foo2 : foo {
static var instance : foo? = nil
init() {
foo2.instance = self
}
func prt() {
print("foo2")
}
}
class foo3 : foo {
static var instance : foo? = nil
init() {
foo3.instance = self
}
}
var f1 : foo = foo1()
f1.prt()
var f2 : foo = foo2()
f2.prt()
var f3 : foo = foo3()
f3.prt()