Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Rachit Arora
Ben Nadel at cf.Objective() 2012 (Minneapolis, MN) with: Rachit Arora@rachitusc )

Thinking About Static vs. Private Methods In TypeScript / Angular 2

By Ben Nadel on

Technically speaking, there's really nothing different about TypeScript. Meaning, it all transpiles down to the same ES5 that we can write today. But, the fact that TypeScript hides certain complexities means that we are more likely to think about the code from a different perspective. Such is the case with static methods. Now that we can seamlessly mix static methods into our class definitions, it's certainly worth taking another look at when we should be using static methods in our TypeScript / Angular 2 classes.


 
 
 

 
 
 
 
 

Run this demo in my JavaScript Demos project on GitHub.

One of the JavaScript features that TypeScript facilitates (through decreased complexity) is class extension. And, now that sub-classing is easier, it's more important that we think about our sub-class / super-class relationships; and, specifically about how the structure of our classes affects inheritance.

In the book Fundamentals Of Object-Oriented Design, Meilir Page-Jones states that a sub-class should never access the private methods of its super-class. The private methods of any class are, just that, private. And, a sub-class shouldn't know about anything more than the public interface already exposed by the super-class.

On a loosely related note, Sandi Metz once talked about a philosophy in which a class shouldn't have private methods at all - that any private methods should be factored out into their own set of classes with cohesive logic and functionality. At first, this sounded very suspect to me; but, the more I've noodled on it, the more it actually makes sense (at least to a good degree).

Bringing this back around to TypeScript, these preceding concepts make me think that I should, when possible, err on the side of pure, static methods over private methods. Not only does this make the methods easier to reason about (since they depend solely on their inputs); but, it means that the functionality can be more easily reused and leveraged by other classes. It also means that when I extend a super-class, I don't have to make assumptions about the private implementation of that super-class.

Now, while static methods can be seamlessly mixed in with the class definition, it doesn't mean that they can be referenced off of the "this" context. Unlike public and private methods, static methods are only available on the Class itself, not on the instances of the class.

To demonstrate this, I've put together a simple demo in which we take the input value from a form control and reverse it. The logic that performs the reversal is provided by a static method on the component, which we consume from within the public methods.

  • // Import the core angular services.
  • import { Component } from "@angular/core";
  •  
  • @Component({
  • selector: "my-app",
  • template:
  • `
  • <input [(ngModel)]="input" (ngModelChange)="handleModelChange()" />
  •  
  • <strong>Reversed:</strong>
  • <span class="reversed">{{ reversedInput }}</span>
  • `
  • })
  • export class AppComponent {
  •  
  • // I hold the input value (for the ngModel).
  • public input: string;
  •  
  • // I hold the value that is a reversed version of the input.
  • public reversedInput: string;
  •  
  •  
  • // I initialize the component.
  • constructor() {
  •  
  • this.input = "";
  • this.reversedInput = "";
  •  
  • }
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I handle changes in the input, calculating the reversed value.
  • public handleModelChange() : void {
  •  
  • // Here, we are using a PRIVATE method to determine some of the businessy logic;
  • // but, we're using a STATIC method to implement some of the more generic logic.
  • // This makes the static functionality available to any class that might want
  • // to sub-class the AppComponent (since private methods really shouldn't be
  • // referenced from a sub-class if it can be avoided, as it increases coupling
  • // between the sub-class and the super-class).
  • // --
  • // NOTE: The STATIC methods could be moved into various utility classes if we
  • // wanted to split them out.
  • this.reversedInput = this.isReversible( this.input )
  • ? AppComponent.reverseString( this.input )
  • : this.input
  • ;
  •  
  • }
  •  
  •  
  • // ---
  • // PRIVATE METHODS.
  • // ---
  •  
  •  
  • // I determine if the given string is long enough to be meaningful in reverse.
  • private isReversible( value: string ) : boolean {
  •  
  • return( value.length > 1 );
  •  
  • }
  •  
  •  
  • // ---
  • // STATIC METHODS.
  • // NOTE: Static methods are available off the Class, not the Instance.
  • // ---
  •  
  •  
  • // I reverse the given string value.
  • static reverseString( value: string ) : string {
  •  
  • return( value.split( "" ).reverse().join( "" ) );
  •  
  • }
  •  
  • }

As you can see, the reverseString() method is static. This means that we have to access it as a method on AppComponent instead of "this". But, this works perfectly well. And, when we run the above code, we get the following output:


 
 
 

 
 Static vs. Private methods in TypeScript / Angular 2 classes. 
 
 
 

To be clear, I am not advocating that we stop using private methods. There are many cases where private methods make the most sense - helping to break down the complexity of internal algorithms. But, I think that there are also many cases were private methods are private simply because they are not part of the "API" of an object. In those cases, I think that those private methods would be best served as static methods on the class; or, factored out of the class completely. This way, that non-instance-specific logic can be leveraged effectively by sub-classes; or, simply by classes that are trying to implement similar yet divergent logic.

Of course, when one class depends on another class, no matter what the mechanism, the relationship increases complexity and coupling within the application. So, all things done in measure.




Reader Comments

one small note, static methods historically start with capital letter
tx for the post

Reply to this Comment

@Sean,

That might be true in other languages (I don't have that much experience). But, at least in JavaScript, they seem to be lower-case. Examples:

Math.max()
String.fromCharCode()
Date.now()

It's possible that JavaScript plays by its own rules :)

Reply to this Comment

I don't think you ever need static methods. It sounds like what you want to do is take a strategy (i.e. reversing the input) and make it accessible in multiple places. You are tackling it from an object-inheritance perspective, but is that the right approach? In the JavaScript world, I think it works better to think of that function as being a strategy that lives on it's own. In other words, why even bother with the static implementation? Why not just export the function directly, and then import it when it is needed? Then you don't have to arbitrarily attach it to something or worry about inheritance or even reference a class just to get to a function - you just reference the function directly.

If a class makes sense to inherit from, then implement that class with methods and properties that can be part of the inheritance chain. If there is a strategy you want to reuse, don't force that into a class or inheritance chain - just declare your strategy, export it, then conveniently import it when needed:

export function reverseString() {
}

....

import {reverseString} from './commonFns';

(now you can use it)

I'm interested in your thoughts. IMHO static methods add complexity and overhead to testing and reliance on classes that may or may not be the right objects to "own" the methods you are trying to reuse.

Reply to this Comment

"a sub-class should never access the private methods of its super-class."

More like a subclass should never be allowed to access the private methods of its parent.

"these preceding concepts make me think that I should, when possible, err on the side of pure, static methods over private methods"

The two concepts are completely different, though. Statics were developed for when you only need one instance of that class or method in the heap. Or for when you should only ever have one instance. I like static classes for factories, for instance. (See what I did there? Static, for instance? Ah, I kill me) And I really like static constructors for expensive operations that set an internal state that should then never change. But this is all vastly different from private methods. Private methods are for hiding functionality. Statics are for making it more easily available. Now, JS may let you get away with a private static., but that doesn't necessarily make it a good idea.

"To be clear, I am not advocating that we stop using private methods. There are many cases where private methods make the most sense - helping to break down the complexity of internal algorithms."

The biggest difference between static and private methods is that statics should never change the internal state of the object. Although that may be less important with Javascript's more forgiving nature. In other languages, this can result in some nasty concurrency issues, which is why in Java and C# you can't even do it. Compiler won't let you.

"But, I think that there are also many cases were private methods are private simply because they are not part of the "API" of an object."

That's exactly the purpose of non-public methods. If nothing outside the object should be able to access it, then the method should be non-public. Whether restricted to the inheritance tree or to the class itself, depending on need.

Yes- you happened to write this at a time when I'm giving very serious thought to the idea of static classes and methods. And I've always been a stickler for the correct access modifiers.

Reply to this Comment

@Jeremy,

Generally speaking, I agree with you are saying. Though, just to clarify, you don't need to extend / inherit-from a class just to use it's Static methods - you would only need to import said class and access it's static methods (since you don't need an "instance" to access them, just the constructor reference). But, that said, if you were an application author, I agree that factoring them out into a module that could then be imported into multiple places makes the most sense.

Of course, if you are a module author (as opposed to an application author), I might just err on the side of simplicity over modularity. That said, everyone seems to be breaking everything up into tiny 5-line modules these days :D

Reply to this Comment

@Matt,

In JavaScript, static methods can't mess with state because they don't have access (at least not inherently) to the instances generated by the constructor. So, you don't have to worry about mucking with state since its not possible.

But, when I talked about some Private method being little more than non-public methods, I meant that they were more about functional implementation and less about business logic. Take the .pluck() method from Lodash, which takes an array of objects and maps it only an array of properties (plucks from said objects). If I didn't have access to Lodash, I might create a private method in a object that needed similar functionality:

private pluck( collection, propertyName ) { .... };

But, this method really doesn't have anything to do with the "logic" of the class - its just a convenience method. That's what I mean about it simply not being part of the "public API". In such a case, the method could easily be factored out and required from some other location (just as Jeremy was suggesting).

Hey, if you're thinking about Object stuff right now, you might have fun reading Elegant Objects by Yegor Bugayenko. I'm just about done with it. He basically calls a lot of the OOP stuff we do "pure evil". Among those sins is Static methods, which is thinks is a horrible horrible no good thing :) He also discusses object inheritance and abstract and final methods.

Some of the book goes way over my head; but, it's a quick read, probably you could finish it in like 3-4 evenings. It's relatively short and a very easy read, technically speaking.

Reply to this Comment

@Ben

"Hey, if you're thinking about Object stuff right now, you might have fun reading Elegant Objects by Yegor Bugayenko."

Preface with the fact that I'm not feeling well and that *always* makes me cranky (Yes- I'm basically 5), but I read a few pages of his book, and it's too hit or miss for me.

He's right in that we need to stop thinking of objects in terms of functionality, but rather in terms of identity. Hal Helms always said that an object is an idealized representation of a real world thing. Start looking at it like that and things like where to put "private pluck( collection, propertyName ) { .... };" become easier to tackle.

That said, his opinion on statics is... well, it's right if you choose to ignore some pretty basic parts of software development. Sure, in pure OOP, statics probably wouldn't be a thing. On the other hand, if I can get away with caching large chunks of data or expensive service connections and only do them once, I'll use statics. Software development isn't about rules, it's about choices.

And the bit about not encapsulating more than four objects? Nonsense. You encapsulate what is appropriate given your needs. Which may not include the high degree of flexibility that large amounts of encapsulation gives. Or maybe it does. But boiling it down to a static number is silly.

And while his opinion of ==/.equals() has a point, he's only looking at it from an OOP standpoint. Something compilers care nothing about. Each object is unique because each object is actually unique on the heap. And having Java somehow derive meaning and equality from the object properties is, at best, dangerous.

But again. Not feeling well. Cranky. Going to go to bed now.

Reply to this Comment

@Matt,

Ha ha, no apologies necessary :) I also hate being sick. I don't actually like to even be around people when I'm sick, so I get it.

If you're curious, I just posted a few more snippets from the book as part of a review:

http://www.bennadel.com/blog/3108-elegant-objects-by-yegor-bugayenko.htm

... but, I will admit that like half the book did go over my head. But, I enjoy the challenge of being challenged on how I think about things. The reality is, I'm no where close to even programming anything like this. My entire world, for the most part, consists of procedural objects that implement transaction scripts. Things like:

projectService.createProject( ... )
userService.createUserAccount( ... )
loggingService.logItem( ... )

... I honestly don't have the first clue about how to start moving that in a direction that uses true "domain objects" to accomplish tasks.

So, really, the best that I can take away from books like this, at the moment, is how to better design smaller portions of the application. Like, if I need to create a gateway / client that talks to a 3rd-party API. Then, at least at that level, I can start to apply some better thinking in how I build and orchestrate those mini-features.

But, even at that level, I still think about it in terms of procedures. Meaning, even with an API client, I'm still instantiating and caching a single instance of it and then using it to talk to the API. It's not like I'm instantiating "API Request" objects for each request and then having them do the work.

So really, I have a terribly long journey to go before I can say that I'm truly internalizing some of these concepts.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.