使用插件化架构来扩展你的模块

1,143 阅读3分钟
原文链接: medium.com

Extending your modules using a plugin architecture

Breaking your app up into modules has many benefits and it is extremely easy to achieve with Xcode using workspaces or Cocoapods. Whether you are working with feature teams or you have a set of reusable modules shared between different applications, one of the design decisions you will need to make is how to communicate between the various modules.

I have never been a fan of using a publish-subscribe pattern because completely decoupled events are harder to trace, debug, and maintain. When our architect suggested using a plugin architecture and having worked with Wordpress earlier in my career, I became quite excited. Wordpress uses a plugin architecture successfully, which allows for extending functionality of Wordpress without touching the Wordpress core itself.

As an example, Wordpress allows you to hook into the <head></head> section of a HTML document using the wp_head() hook. This allows any Wordpress plugin or theme to insert their own custom Javascript or CSS. We can take inspiration from this and extend our own modules using a similar philosophy.

This architecture is extremely easy to implement and I have developed a Playground example in the following repository:

TYRONEMICHAEL/plugin-architecture-swift
plugin-architecture-swift - Extending modules using a plugin architecturegithub.com

An overview

A typical application might be broken down into the following modules depending on the different domains:

  • Login module
  • User profile module
  • Data storage module
  • User feed module
  • and so forth…

Separating an app into modules allows for different teams to work in isolation. You might even have a set of modules you would like to reuse between your various applications that can expose functionality so your other modules can hook into, extend, or react to certain conditions.

A simple example might be to fetch the user’s profile, store the data returned during a login, and fetch the user’s feed. We can do that as soon as the user logs in by fetching all the data we need up front. We will develop and extend a Login module to allow other modules to plug into it as soon as there has been a successful or failed login. We can take inspiration from the image below.

Creating the Login Module

After creating the Login Module inside of our workspace, we will create a very useful class that lets us authenticate successfully and authenticate unsuccessfully.

Now let’s create two public interfaces that any of our modules can conform to in order to plug into our Login Module. We will start off by first creating the protocol for a successful login.

And then for an unsuccessful login.

It’s better to separate the interfaces so the corresponding modules can either conform to one, or both, depending on their needs. Some modules may only be interested in a successful login, while others might be interested in both a successul and unsuccessful login. By grouping the interfaces into one, the interested modules would have to cater for both instances. Separating the interfaces gives us more flexibility.

Now we need some sort of plugin registry. This is where the interested modules will register their implementations against the plugin interfaces we defined above.

Here we create a login container singleton that lets interested modules register their implementations. We explicitly declare our public and internal methods, which is very important. We don’t want other modules to accidentally resolve for the Login Module’s plugins. You might laugh, but I have seen stranger things done before.

Finally we can update our very useful Authenticator class to resolve for its plugins.

Creating Plugins

Now we can create our implementations that conform to the appropriate plugins.

Now all we need to do is register with the login container.

Finally we can test with the following.

And we will get the following response.

Logging in...
Logged in successfully...
Fetching user profile with id 1234...
Fetching user feed with id 1234...
Storing user with id 1234...

Conclusion

We successfully implemented this technique for displaying tabs inside a UITabbar. Each module can register as a plugin and therefore the Tabbar Module will display each of the plugins registered inside the UITabbar as a UITabbarItem. The Tab Module is only responsible for the look and feel and displaying each of the plugins registered against it.

Special thanks to Justin Guedes for the login container implementation, and our software architect, Leo.

All code can be found in the following repository:

TYRONEMICHAEL/plugin-architecture-swift
plugin-architecture-swift - Extending modules using a plugin architecturegithub.com

Follow me on twitter, where I regularly like to tweet about fun topics like this.

Tyrone Avnit (@tyroneavnit) | Twitter
The latest Tweets from Tyrone Avnit (@tyroneavnit). Dad, husband, and Polyglot Programmer. Specialising in iOS…twitter.com