Có cách nào để tạo một lớp trừu tượng trong Ngôn ngữ Swift hay đây là một giới hạn giống như Objective-C? Tôi muốn tạo một lớp trừu tượng có thể so sánh với những gì Java định nghĩa là một lớp trừu tượng.
Có cách nào để tạo một lớp trừu tượng trong Ngôn ngữ Swift hay đây là một giới hạn giống như Objective-C? Tôi muốn tạo một lớp trừu tượng có thể so sánh với những gì Java định nghĩa là một lớp trừu tượng.
Câu trả lời:
Không có các lớp trừu tượng trong Swift (giống như Objective-C). Đặt cược tốt nhất của bạn sẽ là sử dụng Giao thức , giống như Giao diện Java.
Với Swift 2.0, sau đó bạn có thể thêm các cài đặt phương thức và các cài đặt thuộc tính được tính bằng các phần mở rộng giao thức. Hạn chế duy nhất của bạn là bạn không thể cung cấp các biến hoặc hằng thành viên và không có công văn động .
Một ví dụ về kỹ thuật này sẽ là:
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
Lưu ý rằng điều này đang cung cấp "lớp trừu tượng" như các tính năng ngay cả đối với các cấu trúc, nhưng các lớp cũng có thể thực hiện cùng một giao thức.
Cũng lưu ý rằng mỗi lớp hoặc cấu trúc thực hiện giao thức Nhân viên sẽ phải khai báo lại thuộc tính hàng năm.
Quan trọng nhất, lưu ý rằng không có công văn năng động . Khi logSalary
được gọi trên cá thể được lưu trữ dưới dạng SoftwareEngineer
nó sẽ gọi phiên bản ghi đè của phương thức. Khi logSalary
được gọi trong trường hợp sau khi nó được chuyển sang một Employee
, nó gọi thực hiện ban đầu (nó không tự động gửi đến phiên bản bị ghi đè mặc dù thực tế đó là một Software Engineer
.
Để biết thêm thông tin, hãy xem video WWDC tuyệt vời về tính năng đó: Xây dựng ứng dụng tốt hơn với các loại giá trị trong Swift
protocol Animal { var property : Int { get set } }
. Bạn cũng có thể rời khỏi tập hợp nếu bạn không muốn tài sản có setter
func logSalary()
vào khai báo giao thức Nhân viên, ví dụ sẽ in overridden
cho cả hai cuộc gọi đến logSalary()
. Đây là trong Swift 3.1. Do đó bạn có được những lợi ích của đa hình. Phương pháp đúng được gọi trong cả hai trường hợp.
Lưu ý rằng câu trả lời này được nhắm mục tiêu tại Swift 2.0 trở lên
Bạn có thể đạt được hành vi tương tự với các giao thức và phần mở rộng giao thức.
Đầu tiên, bạn viết một giao thức hoạt động như một giao diện cho tất cả các phương thức phải được thực hiện trong tất cả các loại phù hợp với nó.
protocol Drivable {
var speed: Float { get set }
}
Sau đó, bạn có thể thêm hành vi mặc định cho tất cả các loại phù hợp với nó
extension Drivable {
func accelerate(by: Float) {
speed += by
}
}
Bây giờ bạn có thể tạo các loại mới bằng cách thực hiện Drivable
.
struct Car: Drivable {
var speed: Float = 0.0
init() {}
}
let c = Car()
c.accelerate(10)
Vì vậy, về cơ bản bạn nhận được:
Drivable
s thực hiệnspeed
Drivable
( accelerate
)Drivable
được đảm bảo không bị khởi tạo vì nó chỉ là một giao thứcMô hình này thực sự hoạt động giống nhiều đặc điểm hơn, có nghĩa là bạn có thể tuân thủ nhiều giao thức và thực hiện các triển khai mặc định của bất kỳ giao thức nào, trong khi với siêu lớp trừu tượng, bạn bị giới hạn trong hệ thống phân cấp lớp đơn giản.
UICollectionViewDatasource
. Tôi muốn loại bỏ tất cả các mẫu soạn sẵn và gói nó trong giao thức / phần mở rộng riêng biệt và sau đó sử dụng lại bởi nhiều lớp. Trên thực tế, mẫu mẫu sẽ hoàn hảo ở đây, nhưng ...
Tôi nghĩ rằng đây là gần nhất với Java abstract
hoặc C # abstract
:
class AbstractClass {
private init() {
}
}
Lưu ý rằng, để các công cụ private
sửa đổi hoạt động, bạn phải định nghĩa lớp này trong một tệp Swift riêng.
EDIT: Tuy nhiên, mã này không cho phép khai báo một phương thức trừu tượng và do đó buộc thực thi nó.
Cách đơn giản nhất là sử dụng lệnh gọi fatalError("Not Implemented")
vào phương thức trừu tượng (không phải biến) trên phần mở rộng giao thức.
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()
nhưng nó làm! Khóa này bao gồm myMethod
trong khai báo giao thức; Nếu không thì cuộc gọi gặp sự cố.
Sau khi tôi vật lộn trong vài tuần, cuối cùng tôi cũng nhận ra cách dịch một lớp trừu tượng Java / PHP sang Swift:
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());
}
}
Tuy nhiên, tôi nghĩ rằng Apple đã không triển khai các lớp trừu tượng bởi vì nó thường sử dụng mẫu giao thức ủy nhiệm + thay thế. Ví dụ, mẫu tương tự ở trên sẽ được thực hiện tốt hơn như thế này:
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());
}
}
Tôi cần loại mẫu này vì tôi muốn phổ biến một số phương thức trong UITableViewControll như viewWillAppear, v.v ... Điều này có hữu ích không?
Có một cách để mô phỏng các lớp trừu tượng bằng cách sử dụng Giao thức. Đây là một ví dụ:
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
}
}
Một cách nữa để bạn có thể thực hiện lớp trừu tượng là chặn trình khởi tạo. Tôi đã làm theo cách này:
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")
}
}
}
Tôi đã cố gắng tạo một Weather
lớp trừu tượng, nhưng sử dụng các giao thức không lý tưởng vì tôi phải viết cùng một init
phương thức nhiều lần. Việc mở rộng giao thức và viết một init
phương thức có vấn đề, đặc biệt là khi tôi đang sử dụng NSObject
tuân thủ NSCoding
.
Vì vậy, tôi đã đưa ra điều này cho sự NSCoding
phù hợp:
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...
}
Đối với init
:
fileprivate init(param: Any...) {
// Initialize
}
Di chuyển tất cả các tham chiếu đến các thuộc tính và phương thức trừu tượng của lớp Cơ sở sang triển khai mở rộng giao thức, trong đó Tự ràng buộc với lớp Cơ sở. Bạn sẽ có quyền truy cập vào tất cả các phương thức và thuộc tính của lớp Base. Ngoài ra trình biên dịch kiểm tra việc thực hiện các phương thức và thuộc tính trừu tượng trong giao thức cho các lớp dẫn xuất
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;
}
}
Với giới hạn không có công văn động, bạn có thể làm một cái gì đó như thế này:
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()