Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Design Patterns in Swift: Factory
Raphael Martin
Raphael Martin

Posted on • Edited on

Design Patterns in Swift: Factory

The Factory Design Pattern

This is the first post of a series about Design Patterns! And we're starting with a very useful one for Swift applications:Factory.

This pattern consists in centralizing the object creation logic of your application in a proper place, benefiting from theabstraction concept.

If you're feeling a bit lost about abstraction, you can refer tomy post about OOP in Swift.


Problem Context

When adding a newDesign Pattern to your software, you should always think in which problem this pattern solves.
WithFactory, we are usually trying to solve problems related to object creation, but not only it.

Coupled Lego bricks

Coupled Lego bricks

Coupling

Imagine working on a new Social Network app, and in its first version it should supports textual posts only, that will be calledArticles. So, you create a customArtcileView class, with everything that you need for each post:

classArticleView:UIView{privatelazyvarcontentLabel:UILabel={// Defines the label attributes}()privatelazyvarlikeButton:UIButton={// Defines the button attributes}()init(){super.init(frame:.zero)setupView()}requiredinit?(coder:NSCoder){fatalError("init(coder:) has not been implemented")}privatefuncsetupView(){backgroundColor=.white// Add subviewsaddSubview(contentLabel)addSubview(likeButton)// Set up constraintssetupConstraints()}privatefuncsetupConstraints(){// Set the constraints}@objcprivatefuncdidTapLikeButton(){// Make request to service, saving that the current user liked this post}}
Enter fullscreen modeExit fullscreen mode

In an implementation like that, all the logic related to the behavior ofliking posts is centralized in theArticleView. Your business logic now istightly coupled to your view class.

In future, if you decide to implement image posts, with anImageView for example, you'll need to duplicate the logic for the "like" action, and also add logic to determine which class to initialize:

classContentViewManager{funccreateView(forcontentType:String)->UIView{ifcontentType=="article"{returnArticleView()}elseifcontentType=="image"{returnImageView()}else{fatalError("Unsupported content type:\(contentType)")}}}
Enter fullscreen modeExit fullscreen mode

This kind of implementation has several problems, such asviolation of Open/Closed Principle (OCP) andmaking testing harder.


What is the Factory Design Pattern?

Factory is a Design Pattern that usesfactory methods to create different types of objects, that share a same more abstract type, without the need of specifying directly the concrete type of the desired object. So factories are similar to what we saw in the previous example with theContentViewManager class, but without violating any SOLID principle and not coupling your logic.

Code Factory representation

To achieve that,Factory uses a lot the abstraction OOP concept. First, we need an abstract type to represent the objects we're trying to create. In Swift, usuallyprotocols are used, but in other languages you can seeabstract classes as well. Let's see how would it be in our Social Network app example:

protocolLikeablePost:UIView{funclikePost()}classArticleView:UIView,LikeablePost{// ...}classImageView:UIView,LikeablePost{// ...}
Enter fullscreen modeExit fullscreen mode

Then, you also need another abstract type (we're using aprotocol again) for defining how your factories should looks like:

protocolPostFactory{funcmakePost()->LikeablePost}
Enter fullscreen modeExit fullscreen mode

Look that the return type of ourmakePost() function is our abstract type previously created. Now, we are able to createnew factories every time we need to create new children ofLikeablePost:

classArtcileFactory:PostFactory{funcmakePost()->LikeablePost{ArticleView()}}classImageFactory:PostFactory{funcmakePost()->LikeablePost{ImageView()}}
Enter fullscreen modeExit fullscreen mode

Now, your object creation logic isopen for extension, butclosed for modification, since you don't need to change any existing implementation to add support for new types, just extend it.

How to instantiate a Factory

After creating the factories, now you need some logic to decide which factory you should use when needed. For that, you could create aResolver:

classPostFactoryResolver{staticfuncgetFactory(forcontentType:String)->PostFactory{switchcontentType{case"article":returnArticleFactory()case"image":returnImageFactory()default:fatalError("Unsupported content type:\(contentType)")}}}// Usage:letfactory=PostFactoryResolver.getFactory(for:"article")letpostView=factory.makePost()
Enter fullscreen modeExit fullscreen mode

You may have noticed that this approach is very similar to what we had in ourContentViewManager, before implementing theFactory pattern. And that similarity can still violate the OCP since it will need changes when adding new types to the app. However, we have significant improvements compared to the previous implementation:

  1. Isolation: the decision logic for creating the view objects is isolated from the business logic files.
  2. Decoupling: the client code interacts only with the factories, and not with the views. Also, the views now are more testable since they are easy to mock.

A more robust approach

We can still achieve a fully OCP-compliant approach, using a combination ofdynamic registration anddependency injection.
Dynamic Registration is the idea ofregistry object instances in a place with global visibility to be accessed by clients through an identifier. Here's an example:

classFactoryRegistry{privatevarfactories:[String:PostFactory]=[:]funcregisterFactory(forcontentType:String,factory:PostFactory){factories[contentType]=factory}funcgetFactory(forcontentType:String)->PostFactory?{returnfactories[contentType]}}// Usage:letregistry=FactoryRegistry()// Register factories (can be done at app initialization or runtime)registry.registerFactory(for:"article",factory:ArticleFactory())registry.registerFactory(for:"image",factory:ImageFactory())
Enter fullscreen modeExit fullscreen mode

Then, we can usedependency injection to inject the desired factory in the needed class.

letpost=awaitfetchPostFromAPI(withId:"ABC1234")// `post.type` is a String that can be "article" or "image"ifletfactory=registry.getFactory(for:post.type){letpostView=factory.makePost()// Use `postView` here// Add the postView to the view hierarchy}else{print("No factory registered for this content type.")}
Enter fullscreen modeExit fullscreen mode

Now, adding a new content type doesn't require modifying any existing code, just register the new factory.

If in future, you need to support video posts, you'll just need to:

registry.registerFactory(for:"video",factory:VideoFactory())
Enter fullscreen modeExit fullscreen mode

Disadvantages

So far, we have explored howFactory is useful for decoupling, adding flexibility and in some cases, making testing easier. However there are some cons that should be considered when thinking about implementing it in your app:

1. Complexity

Even though the examples we explored here for the post views brought several benefits, you can see that the code is now morecomplex. We created several different new types, including protocols and classes, increasing the size of our code base.

2. Often Not "Self-Documenting"

Without an explicit documentation for it, or yet not using a good naming convention, factories can be hard to understand at first sight. That increases the learning curve for new team members, impacting the general team's performance.

3. Violations of OCP

As we saw today, without a more robust implementation (that brings more complexity), factories can lead to a violation of the Open/Closed Principle.


Conclusion

Today we went through common problems in Software Engineering regarding objects creation and coupling, how theFactory design pattern can help with it, how to implement it and also disadvantages to consider when choosing it.
I hope that adding this new Design Pattern to your toolkit will help you in your software development journey!

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

iOS & tvOS dev into Swift, SwiftUI, Combine, and HLS. Crafting smooth, responsive apps with great UX. Love pushing tech boundaries to build apps that can handle anything thrown their way.
  • Location
    🇧🇷 Brazil
  • Work
    Senior iOS/tvOS Software Engineer
  • Joined

More fromRaphael Martin

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp