Reputation: 5698
There are a lot of questions out there that are similar, but I'm having a hard time finding something that explains exactly what I'm looking for
I have multiple Services
, which handle a generic Data
type. They currently all subclass an semi-"abstract" class, DataService
This is because all of these Services
have the same stored properties:
class DataService<T: Data> {
let id: String
let eventDataProviders: [EventDataProvider]
private let storedDataProviders: [StoredDataProvider]
and their init
are the same as well:
init(id: String, eventDataProviders: [EventDataProvider], storedDataProviders: [StoredDataProvider]) {
self.id = id
self.storedDataProviders = storedDataProviders
self.eventDataProviders = eventDataProviders
self.setupStoredDataProviders()
self.setupEventDataProviders()
}
the setup
methods are also the same
The difference between these classes is how they handle data, currently defined in an "abstract" function
func eventReceivedHandler() -> ((T) -> Void) {
fatalError("DataService does not have eventReceivedHandler defined")
}
Most resources recommend Protocols and Protocol Extensions. Which I would prefer as well, but I think the two things that make that difficult are:
Trying to reduce duplicate code by keeping the stored property declarations in one place, and by sharing an init
The generic type on the class, it doesn't seem straight-forward to maintain that through a protocol
But the problem with this current solution is
eventReceivedHandler
isn't compiler-enforced Is there a correct solution here? I thought this architecture may be common enough, but I've really been struggling finding anything around online, my search queries contain too many overused terms
Upvotes: 0
Views: 288
Reputation: 2671
You might want to use a protocol with an associated type :
protocol Service {
associatedtype T
var eventDataProviders: [EventDataProvider<T>] { get }
var storedDataProviders: [StoredDataProvider<T>] { get }
}
Coupled with generic providers classes :
class EventDataProvider<T> {
}
class StoredDataProvider<T> {
}
And a concrete class :
class DataService<T>: Service {
let id: String
let eventDataProviders: [EventDataProvider<T>]
let storedDataProviders: [StoredDataProvider<T>]
init(id: String, eventDataProviders: [EventDataProvider<T>], storedDataProviders: [StoredDataProvider<T>]) {
self.id = id
self.storedDataProviders = storedDataProviders
self.eventDataProviders = eventDataProviders
self.setupStoredDataProviders()
self.setupEventDataProviders()
}
func setupStoredDataProviders() {
}
func setupEventDataProviders() {
}
}
Would allow you to have DataService
instances handling different types of data eg. :
let data = DataService<Data>(id: "1", eventDataProviders: [EventDataProvider<Data>()], storedDataProviders: [StoredDataProvider<Data>()])
let data2 = DataService<Int>(id: "2", eventDataProviders: [EventDataProvider<Int>()], storedDataProviders: [StoredDataProvider<Int>()])
And you would also gain more type safety as :
let data3 = DataService<Data>(id: "3", eventDataProviders: [EventDataProvider<Data>()], storedDataProviders: [StoredDataProvider<Int>()])
would trigger :
Cannot convert value of type '[EventDataProvider]' to expected argument type '[EventDataProvider<_>]'
Unfortunately, you loose access control as storedDataProviders
is no longer private.
Upvotes: 1
Reputation: 24341
You can create a protocol
that has eventReceivedHandler()
method, i.e.
protocol EventHandler {
func eventReceivedHandler() -> ((Data)->())
}
Since you're defining T: Data
, I don't think there is even a need for generic type T
since always Data
is expected in that place.
Now, you can create your class DataService
as already did,
class DataService {
let id: String
let eventDataProviders: [EventDataProvider]
private let storedDataProviders: [StoredDataProvider]
init(id: String, eventDataProviders: [EventDataProvider], storedDataProviders: [StoredDataProvider]) {
//....
}
func setupStoredDataProviders() {
//.....
}
func setupEventDataProviders() {
//.....
}
}
Now, the Service
classes will be created like,
class Service1: DataService, EventHandler {
func eventReceivedHandler() -> ((Data) -> ()) {
//....
}
}
class Service2: DataService, EventHandler {
func eventReceivedHandler() -> ((Data) -> ()) {
//....
}
}
In case you still have any doubts regarding that, you can ask.
Upvotes: 1