> A class makes that simpler because each of the methods are extension points you can override.
is a strong argument in favour of classes. They're more extensible even if the author doesn't consider it. However - as soon as your class has an implementation like
def to_json(str)
JSONParser.parse(str) # JSONParser is not streamed
end
then you're in trouble. Unless your language supports dynamic lookup of constants, class names feel very much like a global variable that's a pain to change. In Ruby, as of 1.9.3, lookup is lexical so you can't simply define a class-local value for the JSONParser constant.
I don't know the story in other languages - I assume Java has it as you see a great emphasis on dependency injection. If dynamic lookup of constants was present I think classes would be more unintentionally extensible however they were written - as it is, you have to be as careful writing classes for extension as for functional code where you have to manually provide extension points.
In Perl, classes/packages are dynamically scoped. So you can safely localise any monkey-patching. For eg:
use 5.016;
use warnings;
package JSONParser {
sub parse {
my ($self, $str) = @_;
"JSONParser::parse $str";
}
}
package Foo {
sub new { my $class = shift; bless {}, $class }
sub to_json {
my ($self, $str) = @_;
JSONParser->parse($str);
}
}
my $foo = Foo->new;
say $foo->to_json("foo");
{
# OK... I want to amend that JSONParser->parser behaviour
# but just in this scope!
no warnings 'redefine';
local *JSONParser::parse = sub {
my ($self, $str) = @_;
"No Longer JSONParser::parser! $str";
};
say $foo->to_json("bar");
}
say $foo->to_json("baz");
This outputs...
JSONParser::parse foo
No Longer JSONParser::parser! bar
JSONParser::parse baz
In Java, you often see a class JsonClass with a toJson method like you presented and then a StreamingJsonClass with an overriden toJson that streams - this can cause problems with types (because the most obvious return type for toJson when it's written is JsonObject, but you really want an Iterator<JsonObject>), but Ruby/Python have the same problem (only hidden).
It's quite idiomatic Java, though, to remove all static calls from methods, even if you have to use anonymous classes (i.e. extensible method dependencies) to do so, for exactly the reasons you outline.
> A class makes that simpler because each of the methods are extension points you can override.
is a strong argument in favour of classes. They're more extensible even if the author doesn't consider it. However - as soon as your class has an implementation like
then you're in trouble. Unless your language supports dynamic lookup of constants, class names feel very much like a global variable that's a pain to change. In Ruby, as of 1.9.3, lookup is lexical so you can't simply define a class-local value for the JSONParser constant.I don't know the story in other languages - I assume Java has it as you see a great emphasis on dependency injection. If dynamic lookup of constants was present I think classes would be more unintentionally extensible however they were written - as it is, you have to be as careful writing classes for extension as for functional code where you have to manually provide extension points.