- Được cho là một trong những đặc điểm mạnh mẽ và được sử dụng nhiều nhất của
Swift
cho chúng ta mở rộng và phát triển cácprotocol
cũng nhưfunction
mới. Chúng ta không chỉ điều chỉnh ngôn ngữ và cáclibrary
cho phù hợp với các dự án khác nhau mà chúng ta còn có cơ hội để viết các extension có thể được sử dụng lại qua nhiều trường hợp và dự án sử dụng. - Ở bài viết này chúng ta hãy cùng nghiên cứu về vấn đề này cũng như các nguyên tác để có thể ghi nhớ khái quát về
extension
để có thể sử dụng trong nhiều bối cảnh khác nhau.
1/ Generalizing through abstractions:
- Lấy ví dụ chúng ta sẽ cùng nghiên cứu trường hợp cần cải thiện performance, chúng ta viết các
function
cho phép chúng ta dễ dàng lưu trữ bài viết trên ổ cứng:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">extension</span> <span class="token builtin">Article</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">cacheOnDisk</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> folderURLs <span class="token operator">=</span> <span class="token builtin">FileManager</span><span class="token punctuation">.</span><span class="token keyword">default</span><span class="token punctuation">.</span><span class="token function">urls</span><span class="token punctuation">(</span> <span class="token keyword">for</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>cachesDirectory<span class="token punctuation">,</span> <span class="token keyword">in</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>userDomainMask <span class="token punctuation">)</span> <span class="token keyword">let</span> fileName <span class="token operator">=</span> <span class="token string">"Article-<span class="token interpolation"><span class="token delimiter variable">(</span>id<span class="token delimiter variable">)</span></span>.cache"</span> <span class="token keyword">let</span> fileURL <span class="token operator">=</span> folderURLs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">appendingPathComponent</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> <span class="token function">JSONEncoder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span><span class="token keyword">self</span><span class="token punctuation">)</span> <span class="token keyword">try</span> data<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>to<span class="token punctuation">:</span> fileURL<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Việc mở rộng
type
cụ thể vớiextension
trên có thể là cách tốt để giảm việc trùng lặp code vừa giúp thực hiện các tác vụ bình thường trên source code dễ dàng hơn. Tuy nhiên, một phầnextension
đôi khi cũng làm cho code trở nên ít linh hoạt và bị tách rời nhiều hơn. Trong trường hợp này khả năng cao chúng ta sẽ không chỉ lưu giữArticle
mà còn lưu cácmodel
khác nữa.Chúng ta cùng nghiên cứu để cải thiện
extension
có thể tái sử dụng:Encode
từng value trongJSON
, chúng ta cần bất kỳtype
có thể sử dụng tuân thủ libraryEncodable
protocol
.- Chúng ta cũng cần tương thích
id
với cáctype
cùng loại sao cho tính toán được tên tệp duy nhất.
Để thực hiện hai yêu cầu trên chúng ta sử dụng
Encodable
và add mộtgeneric type constraint
để chỉ định cache method của chúng ta có thể được gọi trênIdentifiable
, cung cấp cho chúng ta thuộc tínhid
chúng ta cần:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">extension</span> <span class="token builtin">Encodable</span> <span class="token keyword">where</span> <span class="token keyword">Self</span><span class="token punctuation">:</span> <span class="token builtin">Identifiable</span> <span class="token punctuation">{</span> <span class="token comment">// We also take this opportunity to parameterize our JSON</span> <span class="token comment">// encoder, to enable the users of our new API to pass in</span> <span class="token comment">// a custom encoder, and to make our method's dependencies</span> <span class="token comment">// more clear:</span> <span class="token keyword">func</span> <span class="token function">cacheOnDisk</span><span class="token punctuation">(</span>using encoder<span class="token punctuation">:</span> <span class="token builtin">JSONEncoder</span> <span class="token operator">=</span> <span class="token punctuation">.</span><span class="token keyword">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> folderURLs <span class="token operator">=</span> <span class="token builtin">FileManager</span><span class="token punctuation">.</span><span class="token keyword">default</span><span class="token punctuation">.</span><span class="token function">urls</span><span class="token punctuation">(</span> <span class="token keyword">for</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>cachesDirectory<span class="token punctuation">,</span> <span class="token keyword">in</span><span class="token punctuation">:</span> <span class="token punctuation">.</span>userDomainMask <span class="token punctuation">)</span> <span class="token comment">// Rather than hard-coding a specific type's name here,</span> <span class="token comment">// we instead dynamically resolve a description of the</span> <span class="token comment">// type that our method is currently being called on:</span> <span class="token keyword">let</span> typeName <span class="token operator">=</span> <span class="token function">String</span><span class="token punctuation">(</span>describing<span class="token punctuation">:</span> <span class="token keyword">Self</span><span class="token punctuation">.</span><span class="token keyword">self</span><span class="token punctuation">)</span> <span class="token keyword">let</span> fileName <span class="token operator">=</span> <span class="token string">"<span class="token interpolation"><span class="token delimiter variable">(</span>typeName<span class="token delimiter variable">)</span></span>-<span class="token interpolation"><span class="token delimiter variable">(</span>id<span class="token delimiter variable">)</span></span>.cache"</span> <span class="token keyword">let</span> fileURL <span class="token operator">=</span> folderURLs<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">appendingPathComponent</span><span class="token punctuation">(</span>fileName<span class="token punctuation">)</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> encoder<span class="token punctuation">.</span><span class="token function">encode</span><span class="token punctuation">(</span><span class="token keyword">self</span><span class="token punctuation">)</span> <span class="token keyword">try</span> data<span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>to<span class="token punctuation">:</span> fileURL<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
2/ Xác định đúng protocol:
- Chúng ta viết lại
extension
Article
, lần này chúng ta dùngResult
type để cho phépresult instance
có thể mang một arrayArticle
để có thểcombined
với mộtinstance
cùng loại:
1 2 3 4 5 6 | <span class="token keyword">extension</span> <span class="token builtin">Result</span> <span class="token keyword">where</span> <span class="token builtin">Success</span> <span class="token operator">==</span> <span class="token punctuation">[</span><span class="token builtin">Article</span><span class="token punctuation">]</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">combine</span><span class="token punctuation">(</span>with other<span class="token punctuation">:</span> <span class="token keyword">Self</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> other<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Như
caching method
bên trên, không có yêu cầu nào từ đoạn code trên yêu cầu biết vềArticle
. Trong trường hợp này, chúng ta cầncombine
2 collection value thành một vớiRangeReplaceableCollection
.
1 2 3 4 5 6 | <span class="token keyword">extension</span> <span class="token builtin">Result</span> <span class="token keyword">where</span> <span class="token builtin">Success</span><span class="token punctuation">:</span> <span class="token builtin">RangeReplaceableCollection</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">combine</span><span class="token punctuation">(</span>with other<span class="token punctuation">:</span> <span class="token keyword">Self</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">.</span><span class="token function">success</span><span class="token punctuation">(</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> other<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
3/ Tránh conflic và type polutiton:
- Trong trường hợp chúng ta xây dựng một nơi lưu trữ cho các framework, cho phép các dự án khác nhau có thể lưu và tài các value bằng cách sử dụng
Container
. Chúng ta định nghĩaDataConvertible
protocol để có thể tạo một vài system type như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token keyword">public</span> <span class="token keyword">protocol</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> data<span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">get</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">Data</span><span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">var</span> data<span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">String</span><span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">var</span> data<span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">Data</span><span class="token punctuation">(</span>utf8<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">UIImage</span><span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">var</span> data<span class="token punctuation">:</span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">pngData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">!</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token number">_</span> value<span class="token punctuation">:</span> <span class="token builtin">DataConvertible</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> value<span class="token punctuation">.</span>data <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Cách xử lý trên có thể hoạt động tốt với một dự án độc lập, nhưng nếu chúng ta định chia sẻ chức năng này cho nhiều dự án thì có lẽ các xủ lý này trở nên rắc rối, rườm rà.
Vì chúng ta đã định nghĩa các
property
yêu cầu nhưdata
nên có thể nhiều khi nó sẽ trùng lặp với các định nghĩa khác. Điều đó cũng đúng với protocol có nhưng cái tên chung chung nhưDataConvertible
. Trong khi tên của module có thể sử dụngModuleName.TypeName
, tên thuộc tính thì không thể sử dụng cách này.Có một giải pháp tốt cho vấn đề loại này là loại bỏ
protocol
và thay vào đó là thêm vàotype-specific
choContainer
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token number">_</span> data<span class="token punctuation">:</span> <span class="token builtin">Data</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token number">_</span> string<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token function">Data</span><span class="token punctuation">(</span>string<span class="token punctuation">.</span>utf8<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token number">_</span> image<span class="token punctuation">:</span> <span class="token builtin">UIImage</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> image<span class="token punctuation">.</span><span class="token function">pngData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Error</span><span class="token punctuation">.</span>failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token function">write</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Các tiếp cận trên hoạt động tốt với nhiều loại
type
– điều mà chúng ta mong muốn. Khó khăn xảy ra khi chúng ta muốn thêm nhiều tuỳ chọn cấu hình và tham số vàoAPI container
. - Lấy ví dụ chúng ta muốn được cấp phép cho các
API
củauser
để chỉ định mức độ bền vững sử dụng khi sử dụngvalue
nào đó.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token number">_</span> data<span class="token punctuation">:</span> <span class="token builtin">Data</span><span class="token punctuation">,</span> persistence<span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span>permanent<span class="token punctuation">,</span> tags<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">Tag</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token number">_</span> string<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">,</span> persistence<span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span>permanent<span class="token punctuation">,</span> tags<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">Tag</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token function">Data</span><span class="token punctuation">(</span>string<span class="token punctuation">.</span>utf8<span class="token punctuation">)</span> <span class="token keyword">try</span> <span class="token function">write</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> persistence<span class="token punctuation">:</span> persistence<span class="token punctuation">,</span> tags<span class="token punctuation">:</span> tags <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span><span class="token number">_</span> image<span class="token punctuation">:</span> <span class="token builtin">UIImage</span><span class="token punctuation">,</span> persistence<span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span>permanent<span class="token punctuation">,</span> tags<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">Tag</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> image<span class="token punctuation">.</span><span class="token function">pngData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Error</span><span class="token punctuation">.</span>failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token function">write</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> persistence<span class="token punctuation">:</span> persistence<span class="token punctuation">,</span> tags<span class="token punctuation">:</span> tags <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Vấn đề của chúng ta trước đây là
protocol
của chúng ta dễ gây ra xung đột với quy tắc đặt tên, để xử lý vấn đề này chúng ta sử dụng quy ước đặt tên dài dòng hơn một chút. Chúng ta hãy cũng thực hiện một chức năng choprotocol
có thể tránh việc thay đổi khi chuyển đổi cácUIImage
thànhData
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">public</span> <span class="token keyword">protocol</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">Data</span><span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">String</span><span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">Data</span><span class="token punctuation">(</span>utf8<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">UIImage</span><span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">asContainerData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token function">pngData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Container</span><span class="token punctuation">.</span><span class="token builtin">Error</span><span class="token punctuation">.</span>failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">return</span> data <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Với thay đổi trên
extension
của chúng ta sẽ tránh được việc gây lỗi khi được tái sử dụng trong các project khác nhau. Chúng ta cùng quay lại vấn đềContainer
có methodwrite
có thể handle bất kỳContainerDataConvertible
:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> <span class="token function">write</span><span class="token punctuation">(</span> <span class="token number">_</span> value<span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span><span class="token punctuation">,</span> persistence<span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span>permanent<span class="token punctuation">,</span> tags<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">Tag</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> value<span class="token punctuation">.</span><span class="token function">asContainerData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Vẫn có vấn đề cầ giải quyết, chúng ta cần định nghĩa thêm các
protocol
cácstatic
function để triển khai nó dễ hơn thay vì cứ thêm vào tất cảinstance
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <span class="token keyword">public</span> <span class="token keyword">protocol</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span><span class="token punctuation">(</span><span class="token keyword">for</span> value<span class="token punctuation">:</span> <span class="token keyword">Self</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">Data</span><span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span><span class="token punctuation">(</span><span class="token keyword">for</span> value<span class="token punctuation">:</span> <span class="token builtin">Data</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> value <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">String</span><span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span><span class="token punctuation">(</span><span class="token keyword">for</span> value<span class="token punctuation">:</span> <span class="token builtin">String</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token function">Data</span><span class="token punctuation">(</span>value<span class="token punctuation">.</span>utf8<span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">extension</span> <span class="token builtin">UIImage</span><span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">makeContainerData</span><span class="token punctuation">(</span><span class="token keyword">for</span> value<span class="token punctuation">:</span> <span class="token builtin">UIImage</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token builtin">Data</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> data <span class="token operator">=</span> value<span class="token punctuation">.</span><span class="token function">pngData</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token builtin">Container</span><span class="token punctuation">.</span><span class="token builtin">Error</span><span class="token punctuation">.</span>failedToConvertImageToPNGData <span class="token punctuation">}</span> <span class="token keyword">return</span> data <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Đoạn code trên cho phép chúng ta thay đổi mỗi
write
function mà không cần thêm vào bất kì instance phức tạp nào, bây giờ chúng ta chỉ đơn giản gọimakeContainerData
trực tiếp trên mỗivalue
:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">public</span> <span class="token keyword">struct</span> <span class="token builtin">Container</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">func</span> write<span class="token operator"><</span>T<span class="token punctuation">:</span> <span class="token builtin">ContainerDataConvertible</span><span class="token operator">></span><span class="token punctuation">(</span> <span class="token number">_</span> value<span class="token punctuation">:</span> T<span class="token punctuation">,</span> persistence<span class="token punctuation">:</span> <span class="token builtin">Persistence</span> <span class="token operator">=</span> <span class="token punctuation">.</span>permanent<span class="token punctuation">,</span> tags<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token builtin">Tag</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token keyword">try</span> T<span class="token punctuation">.</span><span class="token function">makeContainerData</span><span class="token punctuation">(</span><span class="token keyword">for</span><span class="token punctuation">:</span> value<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |