2

The Composite Pattern for TypeScript Developers

 3 years ago
source link: https://blog.bitsrc.io/the-composite-pattern-for-typescript-developers-3f8dca1a90f6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The composite pattern explained

This pattern is one of the simplest ones since there is really one component you need to worry about: the composite.

A composite is a structure formed from other composites, together creating a tree-like structure… of composites (recursion, yey!). You can think of it as a directory structure where a root directory can have other directories inside it, but it can also have text files, images and other types of files.

That is a composite. And just like that, you can represent an internal folder structure if you were building an OS. But how can you implement such a concept? What is a “composite”?

Essentially a composite is a structure that you can assign to other structures or that can contain others with the same type. Granted, JavaScript is not great when it comes to types and enforcing a common API could be a problem which is why we’re be using TypeScript instead.

Writing our composite component

Instead of showing a simple class declaration, I’ll use bit for my example to show you how to quickly create a shareble composite component you can use within your projects.

If you don’t know what Bit is, you can check out this introductory article I wrote which covers the basics around it:

The project structure is very simple, just create a folder for the project and inside it a folder called “composite”. Within the folder we’ll create a composite.ts file which will contain our definition and an index.ts file which will export our definition and act as the main entry point.

The relevant code inside composite.ts is the following:

I am exporting two things:

  • The generic interface definition for our composite class.
  • And the actual generic composite class.

I’m using Generics here (notice the <T> there in the definition) to keep the code as reusable as possible. You’ll see in a minute what I mean by that.

The logic inside the class is not really that complex, you can add as much complexity as you need into it, but for the sake of showing you an example, I’m just having a simple addChild method that takes care of adding as many child composite components as you want. And I also have a traverse method because I think it’s important to show at least one way of iterating over the components. After all, this is not a simple list of elements, this is a tree-like structure.

The traversing method I coded is simply going to go “child-first” and then as the last element, it’ll tackle the root component. And by “tackle” I mean that it’ll apply the received function to it.

Now, with this code in your file and bit already installed, you can quickly run the following line on the root of your project:

$ bit init --harmony 

Now modify the newly created workspace.jsonc file so it uses a Node.js environment (if you’re not coding with Node, you can adjust this step to your preference). To do this, open the file and find the teambit.workspace/variants key, and make sure it looks like this:

"teambit.workspace/variants": {
"*": {
"teambit.harmony/node": {}
}
}

Now, just add the component to be tracked by Bit:

$ bit add composite/ --namespace components

And install Jest so we can also create a few tests around our code:

$ bit install jest

With Jest installed (notice I haven’t used NPM yet), create a new file inside your composite folder called composite.spec.ts and add the following code:

I’m testing 2 things there, you could probably come up with more interesting edge cases:

  • I’m testing a basic call to addChild
  • And I’m testing the traversing logic.

But notice the second test, notice how I’m making use of the Generics by defining a custom type called HouseComponent . It only has a single property, but I´m using it to add color to my bed and my sofa. I’m defining a set of custom properties, and then using them as part of the instantiation of my composites, thus turning my “generic composites” into a specific one.

You can use the same code with your own custom types and only worry about the custom properties, the rest of the logic would remain the same. I mean, you can easily create components called “div” and “body” and have them use custom props with attributes such as id or style , sound familiar? You’ve recreated a simplified version of the DOM only by adding a custom type into the mix. Powerful!

To test our code, just run:

$ bit test

You should be seeing something like this as an output:

$ bit test
- loading bit...
- loading aspects...
- running command test [pattern]...
testing total of 1 components in workspace 'my-workspace-name'
testing 1 components with environment teambit.harmony/node

PASS components/composite/composite.spec.ts
Composite component
√ should correctly add a component to the children list (2 ms)
√ should iterate over the full composite children-first (1 ms)

Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 2.16 s
Ran all test suites.
tested 1 components in 3.712 seconds.

By now, you should’ve noticed a few things:

  1. I’ve not used NPM at all so far, and if you’re a Node.js developer, you might be wondering: what’s up with that?
  2. Even though I installed Jest, I never really configured it or specified which files I wanted to test when running bit test .

This is the power of Bit, abstracting you from the basic operations. By using Bit I didn’t have to worry about picking a package manager (by default Bit is using pnpm but you hadn’t noticed, had you?) or about configuring Jest, it just works. In fact, by default it knows that every file inside a component’s folder, ending with .spec.ts is a test file that needs to be tested by Jest.

Cool uh?

Exporting your code for the world

As an added bonus, you can quickly export your code and share it with your team (or teams).

First, you need to tag your component, or in other words, give it a version:

$ bit tag --all 0.0.1

Then, make sure you’re logged in on your terminal, if you’re not, type:

$ bit login

You’ll be redirected to bit.dev where you can sign-in (or sign-up if you haven’t yet).

Once logged-in on the website, you’ll have to create a collection (selecting the Harmony version when asked) called “components” (since that was the namespace we picked before), and then edit the workspace.jsonc file to have the defaultScope key be "[your username].components" , in my case that would be "deleteman.components" .

With that out of the way, export your work with:

$ bit export

You can see the end result here: https://bit.dev/deleteman/components/components/composite

Conclusion

The composite component is super fun to work with and very easy to code. You are more than welcome to import my public component, extend it and use it as you see fit. You’ll probably want to add extra methods to find a specific node or something, but hey, it’s a start.

As for using TypeScript, in this case I think it’s super useful how I was able to quickly create this prototype without worrying about installing anything BUT bit. In fact, I coded the whole thing in TypeScript and I never even had to worry about installing TypeScript on my machine, that’s wild!

Are you a fan of the Composite pattern as well? Leave a comment and share what’s your favorite design pattern, let’s have a good ol’ contest and see who wins!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK