Thinking About Static vs. Private Methods In TypeScript / Angular 2
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:
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.
Want to use code from this post? Check out the license.
Reader Comments
one small note, static methods historically start with capital letter
tx for the post
@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 :)
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.
"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.
@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
@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.
@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.
@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:
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.
I just generally dislike statics coming from a strongly typed language where the specific implementation of the Class and hence its statics gets compiled into the runtime file, so it's literally not possible to swap to a different implementation than the one the client is grabbing onto. This makes testing and meeting continually expanding requirements more difficult.
In JavaScript, we can leverage more sleight of hand when it comes to replacing statics, but I'm not sure it's a good habit to get into. None of us knows what language or type of project we'll be working on in two years, so it may be wiser to keep the wider ecosystem in mind when considering what kind of practice we'll adopt.
@Amy,
I'm trying to think back to what put me onto this topic in the first place. I think I had come across a scenario in Angular 2 in which I had to invoke a piece of functionality in a core class that wasn't public. But, when looking at it in the source code, it wasn't really a class method - it was a pure method that took inputs and returned outputs.
At the time, I had to copy-paste it into my own controller in order to leverage it. Which, of course, would have been much easier if said method was a static method on the core class.
As it turns out, I believe they actually took that method and made it public in a later release. So, in the end, I think this was actually spurred on by a problem in the public API itself. So, perhaps the greater message here is to make public the methods that you can imagine other people wanting to leverage, even if they are not part of the primary API ... assuming, of course, that you don't allow people to change state in an incorrect way.
@Amy
" so it's literally not possible to swap to a different implementation"
That's the point, though. Math.Sqrt is static because there's only one implementation of square root. If you need implementation to change contextually them you shouldn't be looking to statics.
"This makes testing and meeting continually expanding requirements more difficult."
Not really. Statics in a compiled typed language can be unit tested. And if you have to continuously change a static to meet new implementations then maybe a static isn't appropriate. Static Factory, sure but you have to add new methods to an abstract factory whether it's instantiated or static. But the existing methods shouldn't change in response to being used in new contexts.
If developers always designed everything perfectly and/or always had perfect knowledge of upcoming requirements, I'd agree with you. Bit IME people use statics primarily to make them easy to get ahold of. Which means they're actually using them to get around the need to properly design their software.
It's just way too easy to paint yourself into a corner by either hiding too much or hanging too many things in the global space that shouldn't be. And some really major frameworks such as Adobe Flex or .Net are guilty of these. So I think it's safe to say that if such high-powered developers can fall victim to poor design, then it's best to circumscribe yourself with best practices to avoid getting yourself in a mess you can't get out of.
When it comes to JavaScript, I'm curious as to where statics "live". I'm just now learning Angular 2, which is causing me to have to learn TypeScript at the same time. I'd guess that statics live in the window object, which I think most of us avoid using out of an abundance of caution. I think there's a reason Angular hides the window from us, and we go around the dependency injection system at our peril.
I personally believe that if you're going to go around the framework you're using, you need to very thoroughly understand why and how the framework puts that restriction in place and be very sure that your reason to go around the framework is way more compelling than simple convenience. And I think you should have given more than a casual try at accomplishing what you want without going around the framework.
Amy-
"IME people use statics primarily to make them easy to get ahold of."
That could be, and in .Net some code automation tools tend to suggest statics for this purpose. Like all programming principles, though, you have to use them properly to get the correct benefit. Statics can't be subclassed, so you use them when their functionality must be constant through the application. Which can also be done with a sealed class. But there's always only one instance of a static class. For instance, I once needed a queue processor. Having more than one would cause concurrency issues, so I made the class static. That way multiple instances couldn't have multiple processors running. It also insured that any instance using the service ran the processor with the same functionality- also important. Take away either of those, though, and a static wouldn't have been needed.
"I personally believe that if you're going to go around the framework you're using, you need to very thoroughly understand why and how the framework puts that restriction in place"
Sure, but statics aren't "going around" the framework. They've a standard part of certain languages. They're just tools. Good when used correctly, not so much when used improperly.
Hello,
is there a way to create a Class with private and public functions in Typescript?
When i do things like this:
class Hello {
public sayPubHello () {alert(' a public call')}
private sayPrivHello () {alert(' a private call')}
}
var HelloApp = new Hello();
HelloApp.sayPubHello(); // alert a public call
HelloApp.sayPrivHello(); // alert a private call
Typescript make both method as public !?!
If i create native javascriptcode, than i do it like this
function Hello() {
this.sayPubHello = function () {alert(' a public call')}
function sayPrivHello () {alert(' a private call')}
}
var HelloApp = new Hello();
HelloApp.sayPubHello(); // alert a public call
HelloApp.sayPrivHello(); // Error sayPrivHello is undefined
Here the "sayPrivHello"-function is realy private.
How can i do this in Typescript?
Static means which is initialized once for example our main method is initialized only once. Many thanks for sharing this information.
@Stefan,
Public and Private definitions in TypeScript are just for the compile-time checks. Ultimately, everything has to be compiled down to some flavor of JavaScript, which doesn't natively support the concept of public/private methods. As such, some people use the convention of prefixing private methods/properties with "_" as in "this._sayPrivHello()". However, this is just a convention, not a restriction, as you can still call that method publicly. This would be good for things you are going to publish (ie, things that may get used outside of a TypeScript context).
If you truly want to have to have non-accessible methods, you'd probably have to move away from Class-base structure, and move more to the revealing module patterns / factory pattern:
www.bennadel.com/blog/2798-using-method-chaining-with-the-revealing-module-pattern-in-javascript.htm
... where you _return_ the public API and leave the private API hidden inside a closure.
Ben. This is another great article, but I also found this helpful, as it explains things in even simpler terms, that even idiots, like me can understand:
https://toddmotto.com/classes-vs-interfaces-in-typescript
The previous link is about private VS static methods, despite its misleading URL...
One question. When a service is injected and we call a service method, is this similar to calling a static class method?