Please look at my code below. The problem is about method dispatch. I have to say that such explicit type casting looks very ugly. Is there any other case to call configure() for a particular generic class without type casting? In a real project, I've got about 40 classes. This is very annoying (leads to bugs) to write
else if T.self == IntroductionProperties.self {
(self as? Configurator<IntroductionProperties>)?.configure()
}
for each class.
I want to ask the people who develop this language. Is it a good practice to write such code?
// MARK: - Properties
protocol ModuleProperties { }
class BackupCardProperties { }
extension BackupCardProperties: ModuleProperties { }
class IntroductionProperties { }
extension IntroductionProperties: ModuleProperties { }
// MARK: - Configurator
protocol ModuleConfigurator {
func configure()
}
class Configurator<T> where T: ModuleProperties {
var properties: T
init(properties: T) {
self.properties = properties
}
}
extension Configurator: ModuleConfigurator {
func configure() {
print("Configurator extension")
if T.self == BackupCardProperties.self {
(self as? Configurator<BackupCardProperties>)?.configure()
} else if T.self == IntroductionProperties.self {
(self as? Configurator<IntroductionProperties>)?.configure()
} else {
fatalError("not implemented")
}
}
}
extension Configurator where T == BackupCardProperties {
func configure() {
print("Configurator of BackupCardProperties")
}
}
extension Configurator where T == IntroductionProperties {
func configure() {
print("Configurator of IntroductionProperties")
}
}
// MARK: - Example
let configurators: [ModuleConfigurator] = [Configurator(properties: BackupCardProperties()),
Configurator(properties: IntroductionProperties())]
configurators.forEach {
$0.configure()
}
The result is
Configurator extension
Configurator of BackupCardProperties
Configurator extension
Configurator of IntroductionProperties
If you do have associated types or self requirements, then I would look into Type Erasure. In either case, you should just be able to call self.properties.configure()
In that case, you might be able to get away with dropping the main ModuleConfigurator conformance and just use conditional conformances:
extension Configurator: ModuleConfigurator where T == BackupCardProperties {
func configure() {
print("Configurator of BackupCardProperties")
}
}
//etc...
I haven't tried it though, so it may not work...
This still seems like a terrible way to organize your code. I am not sure what is going on in configure(), but if it only affects the properties' model code, it makes sense to keep that organized with each model type. Someone else may have a better idea though.
I can see how this feels a bit backwards, though. Still, there's not an obvious design for a different language feature that leaves the method on Configurator.
I think there are probably lots of other things that are more important to get to first, especially when we don't have a design for whatever this feature would be yet and there is a way to accomplish the same thing. But the forums are the right place to try to work out what such a feature would look like and how it might be implemented.