I tend to avoid extending standard library types unless there's a really, really good reason to do so, and the extension's use and benefit is widespread enough over an entire codebase.
Users come with preexisting mental models of what certain types do, how they behave, and the methods and properties they have. Most of the time those models are consistent because each type deals with a specific concern, and the standard library has standards of consistency that make those models feel predictable and easy to understand.
Extensions can violate those models, especially the more specific and scoped the concerns they're addressing. Not only that, but (as implied by the name) extensions also require users to extend their models. Those two things (violation and extension) aren't evil in and of themselves and people can tolerate a little learning, but without constraints they can make a codebase feel inconsistent, alien, and disorganized, and ideally the exchange should be worth it.
Sometimes asking more questions can help hone in on alternatives. With external URLs, some questions that come to mind are: "external relative to what?" or inversely, "what does local mean?" or "who cares?" (by who, I mean what code and semantics).
If URL externality is a concept specific to the context of School
s, that's a big hint that the code that determines URL externality should be associated with School
, as you've done in your original code, or, if you really wanted, in a School.Utilities
enum.
Typing is also an option (which @ibex10 sort of was pointing towards): you could introduce a type that clarifies a URL's externality, though in my opinion this really only makes sense if there's an app/library-level concept of externality:
struct ExternalURL {
let url: URL
init?(url: URL) {
let isExternal = ...
if isExternal {
self.url = url
} else {
return nil
}
}
}
(This also starts getting into tagged types territory, which has been brought up before many times.)
At the end of the day this is all somewhat subjective, but for me a big factor for this kind of decision is how it slots into the overall ontology of a codebase.