Весьма странно, но модератор тут не разглядел четкого предмета обсуждения. Это конечно, напоминает анекдот:
-- есть мнение, что иммутабельность considered harmful
-- А где предмет обсуждения?
-- давайте обсудим мнение о том, что иммутбельность considered harmful
-- оk
Ну что же, исправляюсь.
Давайте обсудим следующее мнение Стива Декорта:A simple example: Apple's FoundationKit has a NSURLRequest object which can only be initialized with an initialized NSURL instance. NSURL can only be initialized with a URL string. The URL string in the NSURL cannot be changed after initialization and the NSURL cannot be changed after the NSURLRequest.
Now this is ok if you can assume you'll never need to change the URL after it's instantiated or change a NSURLRequest's url after it's instantiated. Unfortunately, these assumptions are wrong.
Consider an Amazon S3 request. Setting up the URL scheme, domain, path and parameters can involve logic specific to the S3 request. Naturally, we'd like something like an S3Request subclass of NSURLRequest that would contain this logic. But as our constructor effectively makes these things immutable by the instance, we can't.
So now we've got all this logic and data which should be handled by the request but can't be. So we can either do the wrong thing by creating giant constructor methods that try to pass in everything, and which invariably make their own false assumptions (e.g. that you'll never need to switch the url scheme from https to http for testing) or we can do the ugly thing by creating a new class that holds all the data which NSURLRequest does but is used to create a NSURLRequest request instance after all it's data is set.
The design decision to use constructors in these frameworks classes has the unseen consequence of limiting our design decisions to a choice between the wrong way or the ugly way and this isn't an isolated case. I've seen many other examples of this problem in the Java and C++ world but I suspect they are particularly common among FP languages which seem to actively encourage this pattern of design.
Final notes: It's not the use of a convenience constructor that is the problem, but that such uses are typically associated with the use of immutable instance state. Some might propose returning a new instance for each mutation as a solution, but this creates even worse code when you consider it potentially has to follow the entire immutable state chain and the consequences for multiple references.