Earlier quoted context omitted.
I'm seriously tired of this argument against getters and setters, just generate them with the IDE some also support collapsible regions with a start and end comment, and forget about them. So considering you can just generate and forget why is it tedious?
If it can be generated, why doesn't the compiler do it for you? This shouldn't be done at the IDE level.
Most solutions to this amount to allowing programmers to intercept field access via getters/setters. This way they can just use public fields and switch to getters/setters when they need to add special behavior without breaking the interface. Pseudocode:
// using methods; no special behavior
class Foo {
private bar
public get_bar() { return bar }
public set_bar(_bar) { bar = _bar }
}
// using methods; special behavior
class Foo {
private bar
public get_bar() { return decode(bar) }
public set_bar(_bar) { bar = encode(_bar) }
}
// using getters/setters; no special behavior
class Foo {
public bar
}
// using methods; special behavior
class Foo {
public get bar { return decode(bar) }
public set bar(_bar) { bar = encode(_bar) }
}
Rather than eliminate complexity from the programmer's workload, this just moves it elsewhere. Instead of tedium, which is something that can be alleviated by simple tooling (copy/paste, editor macros, templated code snippets), you get a constant concern over whether a field access will have unexpected behavior, which is something that can only be alleviated by complex tooling (semantic code analysis).So ultimately this approach adds complexity to the language/compiler, adds complexity to the tooling, and has no effect on the programmer's workload.
I'm trying to think of a better approach and the best I can come up with right now is a macro system in the language (one that makes it very clear where a macro is being expanded, to avoid the same problem of "constant concern"). For example:
macro #generate_accessors(field) {
let paramname = unique_identifier()
return #[
public #[ identifier_from_string("get_" + field.as_string()) ]() { return #[ field ] }
public #[ identifier_from_string("set_" + field.as_string()) ](#[ paramname ]) { #[ field ] = #[ paramname ] }
]
}
// no special behavior
class Foo {
private bar
#generate_accessors(bar)
}
// special behavior
class Foo {
private bar
public get_bar() { return decode(bar) }
public set_bar(_bar) { bar = encode(_bar) }
}
But I haven't seen any language where this is the canonical approach.