Cách thực hành tốt nhất để đặt tên tệp Swift có thêm tiện ích mở rộng cho các đối tượng hiện có là gì?


165

Có thể thêm tiện ích mở rộng cho các loại đối tượng Swift hiện có bằng cách sử dụng tiện ích mở rộng, như được mô tả trong đặc tả ngôn ngữ .

Do đó, có thể tạo các tiện ích mở rộng như:

extension String {
    var utf8data:NSData {
        return self.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
    }
}

Tuy nhiên, cách thực hành đặt tên tốt nhất cho các tệp nguồn Swift có chứa các phần mở rộng như vậy là gì?

Trước đây, quy ước là sử dụng extendedtype+categoryname.mcho loại Objective-C như được thảo luận trong hướng dẫn Objective-C . Nhưng ví dụ Swift không có tên danh mục và gọi nó String.swiftcó vẻ không phù hợp.

Vì vậy, câu hỏi là: với Stringphần mở rộng ở trên , tập tin nguồn swift nên được gọi là gì?


4
Đây không phải là một câu hỏi về codereview - Tôi không quan tâm đến ví dụ cụ thể này, tôi muốn biết quy ước đặt tên nhanh là gì.
AlBlue

2
Không có quy ước đặt tên. Điều duy nhất chúng ta phải tiếp tục là các danh mục từ Objective-C, luôn tuân theo ClassName+ExtensionNameđịnh dạng và tôi không thấy quá nhiều người vẫn đang sử dụng. Bên cạnh đó, tôi thấy rằng cồng kềnh thay vì chỉ xác định các lớp và phần mở rộng cùng nhau, hoặc đặt cho tệp một tên tốt hơn như FooAbleTypesvà xác định các thể hiện trong tổng hợp.
CodaFi

4
không có cách đặt tên được nêu ra. Đây là một suy nghĩ: gộp tất cả các phần mở rộng toàn cầu lại với nhau trong một lần duy nhất Extensions.swift. Bằng cách đó, bạn sẽ không mất dấu vết của họ và những người mới tham gia vào cơ sở mã sẽ ngay lập tức chú ý đến họ. Và tôi muốn giữ các tiện ích mở rộng một lần ở chế độ riêng tư đối với tệp mà chúng cần.
Andrew

1
Như Andrew nói, chưa có thực hành đặt tên tiêu chuẩn nào - do đó câu hỏi này được yêu cầu lấy ý kiến ​​cụ thể để một cộng đồng mới thành lập có thể đưa ra một số ý tưởng được đề xuất.
AlBlue

1
Theo tôi, một tệp extend.swift là cách để đi. Giữ cấu trúc bên trong nó được tổ chức (theo cách riêng của bạn) để tìm thấy những gì bạn cần một cách dễ dàng. Một tập tin dễ dàng sao chép hoặc liên kết đến từ nhiều dự án và không quên công cụ.
Yohst

Câu trả lời:


202

Hầu hết các ví dụ tôi đã thấy bắt chước cách tiếp cận Objective-C. Phần mở rộng ví dụ ở trên sẽ là:

String+UTF8Data.swift

Ưu điểm là quy ước đặt tên giúp dễ hiểu rằng đó là một phần mở rộng và Class nào đang được mở rộng.

Vấn đề với việc sử dụng Extensions.swifthoặc thậm chí StringExtensions.swiftlà không thể suy ra mục đích của tệp theo tên của nó mà không nhìn vào nội dung của nó.

Sử dụng xxxable.swiftcách tiếp cận như được sử dụng bởi Java hoạt động ổn đối với các giao thức hoặc phần mở rộng chỉ xác định các phương thức. Nhưng một lần nữa, ví dụ trên định nghĩa một thuộc tính sao cho UTF8Dataable.swiftkhông có nhiều ý nghĩa ngữ pháp.


1
Mặc dù người ta có thể suy ra những gì đang được mở rộng bởi quy ước đặt tên, nhưng đó là một sự phức tạp không cần thiết. Thay vì hàng tấn tệp <name> + <extection> .swift, tôi giữ một tệp extend.swift mà tôi thường sử dụng cho mỗi dự án. Các tập tin nội bộ được tổ chức sao cho việc tìm một lớp cụ thể được mở rộng là dễ dàng.
Yohst

18
Câu trả lời này, <name> + <extection> .swift, thực sự là cách Xcode thực hiện khi tạo các lớp con NSManagedObject cho Dữ liệu lõi trong Xcode 8. Ví dụ: Foo + CoreDataProperIES.swift.
Jerry Krinock

4
Điều gì xảy ra nếu phần mở rộng thực hiện nhiều phương thức?
AlexVPerl

2
Chỉ cần mô tả càng tốt. Ví dụ: Nếu bạn có tiện ích mở rộng cho Hình ảnh bao gồm các chức năng khác nhau để áp dụng các bộ lọc, hãy đặt tên cho nó là Image + Filter.swift. Sử dụng các tệp khác nhau cho các nhóm liên quan trên các chức năng mở rộng là tốt. Nhóm những thứ liên quan với nhau, nhưng giữ những thứ không liên quan riêng biệt. Cuộc sống sẽ tốt đẹp.
picciano

Nếu bạn đang sử dụng quy ước ExtendedType+Functionality.swift, thì có tốt không khi sắp xếp tất cả các Stringtiện ích mở rộng, ví dụ, vào thư mục con riêng của chúng (tức là Stringhoặc String Extensions) trong Extensionsthư mục? Hoặc tốt hơn là chỉ lưu trữ tất cả các tệp mở rộng ở cùng cấp độ trong Extensionsthư mục?
Noah Wilder

8

Không có quy ước Swift. Giữ cho nó đơn giản:

StringExtensions.swift

Tôi tạo một tệp cho mỗi lớp tôi đang mở rộng. Nếu bạn sử dụng một tệp duy nhất cho tất cả các tiện ích mở rộng, nó sẽ nhanh chóng trở thành một khu rừng.


8
Điều này dường như không thể tái sử dụng.
Keller

1
So với?
Mike Cheesne

3
So với một tệp đơn lẻ (hoặc được liên kết chặt chẽ) của các phần mở rộng lớp phục vụ cho một mục đích duy nhất (hoặc có thể liên quan rõ ràng). Một cái gì đó như "StringExtensions" nghe có vẻ như có thể chứa mọi thứ từ mục đích chung Vệ sinh chuỗi đến logic dành riêng cho ứng dụng - có thể không phải là cách tiếp cận tốt nhất nếu sử dụng lại là một vấn đề đáng lo ngại. Quy ước đặt tên ca cao dựa vào chức năng, hơn là thực hiện. Tôi cho rằng "StringExtensions" chỉ ra cái sau. Đặt quy ước sang một bên, tôi thích câu trả lời được chấp nhận, chắc chắn là trong ObjC, nhưng trong Swift có vẻ như là một cách tiếp cận thậm chí tốt hơn do các mô-đun.
Keller

2
Điều đó có ý nghĩa. Tôi đã suy nghĩ nhiều hơn về một ứng dụng duy nhất trong đó việc sử dụng lại không phải là vấn đề đáng lo ngại. Ví dụ: giả sử tôi có một vài hàm chuỗi không liên quan mà tôi muốn sử dụng làm tiện ích mở rộng - tôi có thể tạo một tệp và đặt tất cả các hàm này ở đó hoặc tạo một tệp cho mỗi hàm. Tôi thích sự đơn giản của một tập tin duy nhất trong trường hợp đó. Nhưng lý luận của bạn là âm thanh. Cảm ơn.
Mike Cheesne

Điều này hoàn toàn có ý nghĩa, với điều kiện những thứ được thêm vào đây sẽ tự nhiên áp dụng cho tất cả các chuỗi (ví dụ 'trimRight ()' làm ví dụ). Nếu đó là một cái gì đó cụ thể hơn cho từng trường hợp sử dụng (ví dụ: 'formatAccountNumber ()'), thì tệp phải là 'Chuỗi + AccountFormatted.swift' và nó chỉ được đặt trong phạm vi mà nó thực sự được sử dụng để không làm lộn xộn API bề mặt 'Chuỗi' ở nơi khác.
Mark A. Donohoe

1

Tôi thích StringExtensions.swiftcho đến khi tôi thêm quá nhiều thứ để chia tập tin thành một cái gì đó như String+utf8Data.swiftString+Encrypt.swift.

Một điều nữa, để kết hợp các tệp tương tự thành một sẽ làm cho tòa nhà của bạn nhanh hơn. Tham khảo Tối ưu hóa-Swift-Build-Times


1
Đó là có hai quy ước đặt tên tệp cho cùng một thứ. Tôi nghĩ đó là xấu.
ý nghĩa-vấn đề

@ ý nghĩa-vấn đề Nó phụ thuộc. Hai quy ước đặt tên đều nổi tiếng và được đề xuất bởi Apple Documents. Làm như bạn muốn.
DawnSong

Tôi muốn nhiều lập trình viên sẽ phấn đấu cho sự thanh lịch bằng cách hạn chế các biến thể đặt tên và mã [định dạng].
ý nghĩa-vấn đề

@ nghĩa-vấn đề Sự thanh lịch có hai mặt, nó giống như một vấn đề gây tranh cãi cổ điển về cách viết dấu ngoặc nhọn trong các ngôn ngữ giống như C. Điều đó không quan trọng, vì vậy tôi không nghĩ cần phải chọn một thứ và bắt buộc nó cho đến khi hầu hết mọi người đồng ý làm như vậy.
DawnSong

Tôi có nghĩa là sự thanh lịch của tính nhất quán: sử dụng một cách để đặt tên cho các phần mở rộng hoặc một cách để đặt dấu ngoặc nhọn. Sau đó, tôi nghĩ rằng có một sự khác biệt có thể đo được về khả năng đọc của các kiểu nẹp xoăn khác nhau; vì vậy tôi không nghĩ nó 'tầm thường' chút nào.
ý nghĩa-vấn đề

0

Nếu bạn có một tập hợp các cải tiến phổ biến và linh tinh theo nhóm đã đồng ý, hãy kết hợp chúng lại với nhau như một Phần mở rộng.swift hoạt động như một giải pháp cấp đầu tiên Keep-It-Simple. Tuy nhiên, khi độ phức tạp của bạn tăng lên hoặc các phần mở rộng trở nên liên quan hơn, thì cần có một hệ thống phân cấp để gói gọn sự phức tạp. Trong trường hợp như vậy tôi khuyên bạn nên thực hành sau đây với một ví dụ.

Tôi đã có một lớp học nói chuyện với back-end của tôi, được gọi Server. Nó bắt đầu phát triển lớn hơn để bao gồm hai ứng dụng mục tiêu khác nhau. Một số người thích một tệp lớn nhưng chỉ cần phân tách hợp lý với các tiện ích mở rộng. Sở thích của tôi là giữ mỗi tệp tương đối ngắn nên tôi chọn giải pháp sau. Serverban đầu tuân thủ CloudAdapterProtocolvà thực hiện tất cả các phương pháp của nó. Những gì tôi đã làm là biến giao thức thành một hệ thống phân cấp, bằng cách làm cho nó tham chiếu đến các giao thức phụ:

protocol CloudAdapterProtocol: ReggyCloudProtocol, ProReggyCloudProtocol {
    var server: CloudServer {
        get set
    }
    func getServerApiVersion(handler: @escaping (String?, Error?) -> Swift.Void)
}

Trong Server.swifttôi có

import Foundation
import UIKit
import Alamofire
import AlamofireImage

class Server: CloudAdapterProtocol {
.
.
func getServerApiVersion(handler: @escaping (String?, Error?) -> Swift.Void) {
.
.
}

Server.swiftsau đó chỉ cần thực hiện API máy chủ lõi để thiết lập máy chủ và nhận phiên bản API. Công việc thực sự được chia thành hai tệp:

Server_ReggyCloudProtocol.swift
Server_ProReggyCloudProtocol.swift

Chúng thực hiện các giao thức tương ứng.

Điều đó có nghĩa là bạn cần phải có các khai báo nhập khẩu trong các tệp khác (đối với Alamofire trong ví dụ này) nhưng đó là một giải pháp rõ ràng về mặt cách ly các giao diện theo quan điểm của tôi.

Tôi nghĩ cách tiếp cận này hoạt động tốt như nhau với các lớp được chỉ định bên ngoài cũng như của riêng bạn.


0

Tại sao điều này thậm chí là một cuộc tranh luận? Tôi có nên đặt tất cả các lớp con của mình vào một tệp có tên _Subgroupes.swift. Tôi nghĩ là không. Swift có khoảng cách tên dựa trên mô-đun. Để mở rộng một lớp Swift nổi tiếng cần một tệp dành riêng cho mục đích của nó. Tôi có thể có một nhóm lớn tạo ra một tệp là UIViewExtensions.swift không có mục đích và sẽ gây nhầm lẫn cho các nhà phát triển và có thể dễ dàng bị trùng lặp trong dự án không xây dựng. Quy ước đặt tên Objective-C hoạt động tốt và cho đến khi Swift có khoảng cách tên thật, đó là cách tốt nhất để đi.


Trong trường hợp của tôi, tôi nghĩ thật hợp lý khi có một tệp có tên UIViewExtensions.swift cung cấp các phần mở rộng được xác định trong tệp đó sẽ có ý nghĩa đối với bất kỳ / tất cả các lớp UIView, như phương thức 'placeIn (UIView)'. Nếu nó là dành riêng cho sử dụng (nghĩa là chỉ dành cho một phần của ứng dụng, giả sử xung quanh trang trí chế độ xem tùy chỉnh, thì tôi sẽ thực hiện UIView + CustomDecor.swift. Điểm là bạn phải xem xét việc sử dụng trước khi thực hiện khái quát hóa như nói một tệp có tên 'UIViewExtensions .swift không thể hiện mục đích 'khi mục đích là phần mở rộng chung cho tất cả các UIView.
Mark A. Donohoe

0

Thay vì thêm ý kiến ​​của tôi ở khắp mọi nơi, tôi sẽ lướt tất cả chúng ở đây trong một câu trả lời.

Cá nhân, tôi thực hiện một cách tiếp cận lai mang lại cả khả năng sử dụng tốt và rõ ràng, đồng thời không làm lộn xộn diện tích bề mặt API cho đối tượng mà tôi đang mở rộng.

Ví dụ, bất cứ điều gì có ý nghĩa có sẵn cho bất kỳ chuỗi nào sẽ đi vào StringExtensions.swiftnhư trimRight()removeBlankLines().

Tuy nhiên, nếu tôi có chức năng mở rộng như formatAsAccountNumber()nó sẽ không xuất hiện trong tệp đó vì 'Số tài khoản' không phải là thứ sẽ tự nhiên áp dụng cho bất kỳ / tất cả các chuỗi và chỉ có ý nghĩa trong ngữ cảnh của tài khoản. Trong trường hợp đó, tôi sẽ tạo một tệp có tên Strings+AccountFormatting.swifthoặc thậm chí Strings+CustomFormatting.swiftvới một formatAsAccountNumber()hàm nếu có một số loại / cách để thực sự định dạng nó.

Trên thực tế, trong ví dụ cuối cùng đó, tôi đã chủ động không cho nhóm của mình sử dụng các tiện ích mở rộng như thế ở nơi đầu tiên, và thay vào đó sẽ khuyến khích một cái gì đó giống như điều đó AccountNumberFormatter.format(String)thay vào đó không chạm vào Stringdiện tích bề mặt API, vì nó không nên. Trường hợp ngoại lệ sẽ là nếu bạn xác định tiện ích mở rộng đó trong cùng một tệp được sử dụng, nhưng sau đó nó sẽ không có tên tệp riêng.


0

Tôi thích có một +gạch chân thực tế nó chứa các phần mở rộng:

String+Extensions.swift

Và nếu tệp quá lớn, bạn có thể chia nó cho từng mục đích:

String+UTF8Data.swift

String+Encrypt.swift

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.