Swift Mediator Pattern - Collection of generic protocols
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I am trying to implement a general-purpose Mediator pattern in Swift and have the following protocols and classes:
protocol Request {
}
protocol Handler {
associatedtype TRequest = Request
func handle(_ request: TRequest)
}
class RequestProcessor {
func register<THandler: Handler>(_ handler: THandler) {
}
func handle(_ request: Request) {
}
}
With the intended usage being (for example):
struct LoginRequest: Request {
let username: String
let password: String
}
struct LogoutRequest: Request {
let userId: Int
}
class LoginHandler: Handler {
func handle(_ request: LoginRequest) {
// do something
}
}
class LogoutHandler: Handler {
func handle(_ request: LogoutRequest) {
// do something
}
}
// Setup the processor and register handlers
let processor = RequestProcessor()
processor.register(LoginHandler())
processor.register(LogoutHandler())
// The processor handles any kind of Request, in this case a LoginRequest
processor.handle(LoginRequest(username: "steve", password: "..."))
// The LoginHandler's handle method will be called
However I'm not sure how to store the collection of Handler
objects since it is a protocol with associated type. I am aware of type-erasure and have read several answers here as well as various articles on the subject (1, 2) but am unsure how to apply it to my situation.
swift generics design-patterns mediator
add a comment |
I am trying to implement a general-purpose Mediator pattern in Swift and have the following protocols and classes:
protocol Request {
}
protocol Handler {
associatedtype TRequest = Request
func handle(_ request: TRequest)
}
class RequestProcessor {
func register<THandler: Handler>(_ handler: THandler) {
}
func handle(_ request: Request) {
}
}
With the intended usage being (for example):
struct LoginRequest: Request {
let username: String
let password: String
}
struct LogoutRequest: Request {
let userId: Int
}
class LoginHandler: Handler {
func handle(_ request: LoginRequest) {
// do something
}
}
class LogoutHandler: Handler {
func handle(_ request: LogoutRequest) {
// do something
}
}
// Setup the processor and register handlers
let processor = RequestProcessor()
processor.register(LoginHandler())
processor.register(LogoutHandler())
// The processor handles any kind of Request, in this case a LoginRequest
processor.handle(LoginRequest(username: "steve", password: "..."))
// The LoginHandler's handle method will be called
However I'm not sure how to store the collection of Handler
objects since it is a protocol with associated type. I am aware of type-erasure and have read several answers here as well as various articles on the subject (1, 2) but am unsure how to apply it to my situation.
swift generics design-patterns mediator
I would removeassociatedType
constraint fromHandler
so it will beprotocol Handler { func handle(_ request: Request) }
. Now we can declare anarray
ofHandlers
insideRequestProcessor
.
– Kamran
Jan 3 at 13:33
add a comment |
I am trying to implement a general-purpose Mediator pattern in Swift and have the following protocols and classes:
protocol Request {
}
protocol Handler {
associatedtype TRequest = Request
func handle(_ request: TRequest)
}
class RequestProcessor {
func register<THandler: Handler>(_ handler: THandler) {
}
func handle(_ request: Request) {
}
}
With the intended usage being (for example):
struct LoginRequest: Request {
let username: String
let password: String
}
struct LogoutRequest: Request {
let userId: Int
}
class LoginHandler: Handler {
func handle(_ request: LoginRequest) {
// do something
}
}
class LogoutHandler: Handler {
func handle(_ request: LogoutRequest) {
// do something
}
}
// Setup the processor and register handlers
let processor = RequestProcessor()
processor.register(LoginHandler())
processor.register(LogoutHandler())
// The processor handles any kind of Request, in this case a LoginRequest
processor.handle(LoginRequest(username: "steve", password: "..."))
// The LoginHandler's handle method will be called
However I'm not sure how to store the collection of Handler
objects since it is a protocol with associated type. I am aware of type-erasure and have read several answers here as well as various articles on the subject (1, 2) but am unsure how to apply it to my situation.
swift generics design-patterns mediator
I am trying to implement a general-purpose Mediator pattern in Swift and have the following protocols and classes:
protocol Request {
}
protocol Handler {
associatedtype TRequest = Request
func handle(_ request: TRequest)
}
class RequestProcessor {
func register<THandler: Handler>(_ handler: THandler) {
}
func handle(_ request: Request) {
}
}
With the intended usage being (for example):
struct LoginRequest: Request {
let username: String
let password: String
}
struct LogoutRequest: Request {
let userId: Int
}
class LoginHandler: Handler {
func handle(_ request: LoginRequest) {
// do something
}
}
class LogoutHandler: Handler {
func handle(_ request: LogoutRequest) {
// do something
}
}
// Setup the processor and register handlers
let processor = RequestProcessor()
processor.register(LoginHandler())
processor.register(LogoutHandler())
// The processor handles any kind of Request, in this case a LoginRequest
processor.handle(LoginRequest(username: "steve", password: "..."))
// The LoginHandler's handle method will be called
However I'm not sure how to store the collection of Handler
objects since it is a protocol with associated type. I am aware of type-erasure and have read several answers here as well as various articles on the subject (1, 2) but am unsure how to apply it to my situation.
swift generics design-patterns mediator
swift generics design-patterns mediator
edited Jan 3 at 13:29
Steve Wilford
asked Jan 3 at 12:14
Steve WilfordSteve Wilford
7,13242861
7,13242861
I would removeassociatedType
constraint fromHandler
so it will beprotocol Handler { func handle(_ request: Request) }
. Now we can declare anarray
ofHandlers
insideRequestProcessor
.
– Kamran
Jan 3 at 13:33
add a comment |
I would removeassociatedType
constraint fromHandler
so it will beprotocol Handler { func handle(_ request: Request) }
. Now we can declare anarray
ofHandlers
insideRequestProcessor
.
– Kamran
Jan 3 at 13:33
I would remove
associatedType
constraint from Handler
so it will be protocol Handler { func handle(_ request: Request) }
. Now we can declare an array
of Handlers
inside RequestProcessor
.– Kamran
Jan 3 at 13:33
I would remove
associatedType
constraint from Handler
so it will be protocol Handler { func handle(_ request: Request) }
. Now we can declare an array
of Handlers
inside RequestProcessor
.– Kamran
Jan 3 at 13:33
add a comment |
1 Answer
1
active
oldest
votes
First, the standard advice:
I am trying to implement a general-purpose Mediator pattern in Swift
Don't. Start with the actual problem you're trying to solve, and design good and necessary abstractions for that problem. Don't create generic things just to be generic. Swift will bite you over and over again. Even stdlib, which really needs super-generic things, often has to step outside of pure Swift to pull it off (using compiler special knowledge, and gyb templating). "Being generic" is not a goal in itself. You're almost certainly making this too complicated. Everyone does.
OK, that's out of the way. Second piece of advice: This is not a good use of a protocol with associated types (PAT). The point of a PAT is to add methods to types, not to be types. You never pass Collection
itself or store things of that "type." You create methods that can generically work on any type that is a Collection
. There is no such type as [Collection]
.
The fundamental problem with your approach is that there's no way to implement RequestProcessor.process()
without resorting to as?
casting, which breaks the point of type safety. How does processor
know to call LoginHandler.process
? Why that one? What if two different handlers accepted LoginRequest
? What if no handler accepts that type?
What you've designed here isn't the Mediator pattern. The Mediator pattern joins together colleagues who share a single interface, so it would look like this:
class RequestProcessor<Request> {
var handlers: [(Request) -> Void] =
func register(handler: @escaping (Request) -> Void) {
handlers.append(handler)
}
func handle(request: Request) {
for handler in handlers {
handler(request)
}
}
}
And you'd have a RequestProcessor
for each kind of request, not a generic "processor of every kind of request." Creating a generic one necessarily (in Swift) removes type safety, in which case you're basically creating a slightly Swiftier NotificationCenter
. (It's possible to create a type-safe version of this, but it requires dependent types, which is a quite complex type feature that Swift doesn't have.)
OK, so maybe you really want this central hub and who needs type safety? Why not? You just have to say what you mean, which is that any handler has to be able to accept any request, even if it doesn't act on it. The compiler can't prove anything more specific than that because at compile time it doesn't know the types. So fine, as?
it to death.
protocol Request {}
protocol Handler {
func canHandle(_ request: Request) -> Bool
func handle(_ request: Request)
}
class RequestProcessor {
private var handlers: [Handler] =
func register(_ handler: Handler) {
handlers.append(handler)
}
func handle(_ request: Request) {
for handler in handlers where handler.canHandle(request) {
handler.handle(request)
}
}
}
class LoginHandler: Handler {
func canHandle(_ request: Request) -> Bool {
return request is LoginRequest
}
func handle(_ request: Request) {
guard let loginRequest = request as? LoginRequest else { return }
// handle loginRequest
}
}
But I'd almost certainly get rid of the Mediator pattern. If the goal is to swap in and out processors for testing or whatnot, I'd just use typical dependency injection techniques. Pass the LoginHandler
to whatever method creates the LoginRequest
.
Thanks for this detailed answer, I appreciate the push away from potentially going down completely the wrong path. The reasoning is purely academic, I'm not trying to solve a specific problem, rather trying to port the .NET Mediatr library to Swift, with a bit of learning thrown in for good measure. I think I can live with a variation of the last example you gave but pulling theguard
/as?
clauses into the processor and have the handlers be explicit about the request they handle, probably with a base class rather than a protocol.
– Steve Wilford
Jan 3 at 16:19
I think you'll find you can't move theas?
clause to the caller, because anyHandler
must implement somehandle
method that accepts the same type. You can't call a method using a type that isn't known at compile-time. While an interesting problem, I would recommend against porting a framework that solves a C# problem (likely driven by the pre-3.0 lack of first-class functions), and instead reimagine the underlying problem using Swift tools (particularly first-class functions), and focusing on Swift problems. Most "patterns" are not nearly as universal as they claim to be.
– Rob Napier
Jan 3 at 16:29
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54022102%2fswift-mediator-pattern-collection-of-generic-protocols%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
First, the standard advice:
I am trying to implement a general-purpose Mediator pattern in Swift
Don't. Start with the actual problem you're trying to solve, and design good and necessary abstractions for that problem. Don't create generic things just to be generic. Swift will bite you over and over again. Even stdlib, which really needs super-generic things, often has to step outside of pure Swift to pull it off (using compiler special knowledge, and gyb templating). "Being generic" is not a goal in itself. You're almost certainly making this too complicated. Everyone does.
OK, that's out of the way. Second piece of advice: This is not a good use of a protocol with associated types (PAT). The point of a PAT is to add methods to types, not to be types. You never pass Collection
itself or store things of that "type." You create methods that can generically work on any type that is a Collection
. There is no such type as [Collection]
.
The fundamental problem with your approach is that there's no way to implement RequestProcessor.process()
without resorting to as?
casting, which breaks the point of type safety. How does processor
know to call LoginHandler.process
? Why that one? What if two different handlers accepted LoginRequest
? What if no handler accepts that type?
What you've designed here isn't the Mediator pattern. The Mediator pattern joins together colleagues who share a single interface, so it would look like this:
class RequestProcessor<Request> {
var handlers: [(Request) -> Void] =
func register(handler: @escaping (Request) -> Void) {
handlers.append(handler)
}
func handle(request: Request) {
for handler in handlers {
handler(request)
}
}
}
And you'd have a RequestProcessor
for each kind of request, not a generic "processor of every kind of request." Creating a generic one necessarily (in Swift) removes type safety, in which case you're basically creating a slightly Swiftier NotificationCenter
. (It's possible to create a type-safe version of this, but it requires dependent types, which is a quite complex type feature that Swift doesn't have.)
OK, so maybe you really want this central hub and who needs type safety? Why not? You just have to say what you mean, which is that any handler has to be able to accept any request, even if it doesn't act on it. The compiler can't prove anything more specific than that because at compile time it doesn't know the types. So fine, as?
it to death.
protocol Request {}
protocol Handler {
func canHandle(_ request: Request) -> Bool
func handle(_ request: Request)
}
class RequestProcessor {
private var handlers: [Handler] =
func register(_ handler: Handler) {
handlers.append(handler)
}
func handle(_ request: Request) {
for handler in handlers where handler.canHandle(request) {
handler.handle(request)
}
}
}
class LoginHandler: Handler {
func canHandle(_ request: Request) -> Bool {
return request is LoginRequest
}
func handle(_ request: Request) {
guard let loginRequest = request as? LoginRequest else { return }
// handle loginRequest
}
}
But I'd almost certainly get rid of the Mediator pattern. If the goal is to swap in and out processors for testing or whatnot, I'd just use typical dependency injection techniques. Pass the LoginHandler
to whatever method creates the LoginRequest
.
Thanks for this detailed answer, I appreciate the push away from potentially going down completely the wrong path. The reasoning is purely academic, I'm not trying to solve a specific problem, rather trying to port the .NET Mediatr library to Swift, with a bit of learning thrown in for good measure. I think I can live with a variation of the last example you gave but pulling theguard
/as?
clauses into the processor and have the handlers be explicit about the request they handle, probably with a base class rather than a protocol.
– Steve Wilford
Jan 3 at 16:19
I think you'll find you can't move theas?
clause to the caller, because anyHandler
must implement somehandle
method that accepts the same type. You can't call a method using a type that isn't known at compile-time. While an interesting problem, I would recommend against porting a framework that solves a C# problem (likely driven by the pre-3.0 lack of first-class functions), and instead reimagine the underlying problem using Swift tools (particularly first-class functions), and focusing on Swift problems. Most "patterns" are not nearly as universal as they claim to be.
– Rob Napier
Jan 3 at 16:29
add a comment |
First, the standard advice:
I am trying to implement a general-purpose Mediator pattern in Swift
Don't. Start with the actual problem you're trying to solve, and design good and necessary abstractions for that problem. Don't create generic things just to be generic. Swift will bite you over and over again. Even stdlib, which really needs super-generic things, often has to step outside of pure Swift to pull it off (using compiler special knowledge, and gyb templating). "Being generic" is not a goal in itself. You're almost certainly making this too complicated. Everyone does.
OK, that's out of the way. Second piece of advice: This is not a good use of a protocol with associated types (PAT). The point of a PAT is to add methods to types, not to be types. You never pass Collection
itself or store things of that "type." You create methods that can generically work on any type that is a Collection
. There is no such type as [Collection]
.
The fundamental problem with your approach is that there's no way to implement RequestProcessor.process()
without resorting to as?
casting, which breaks the point of type safety. How does processor
know to call LoginHandler.process
? Why that one? What if two different handlers accepted LoginRequest
? What if no handler accepts that type?
What you've designed here isn't the Mediator pattern. The Mediator pattern joins together colleagues who share a single interface, so it would look like this:
class RequestProcessor<Request> {
var handlers: [(Request) -> Void] =
func register(handler: @escaping (Request) -> Void) {
handlers.append(handler)
}
func handle(request: Request) {
for handler in handlers {
handler(request)
}
}
}
And you'd have a RequestProcessor
for each kind of request, not a generic "processor of every kind of request." Creating a generic one necessarily (in Swift) removes type safety, in which case you're basically creating a slightly Swiftier NotificationCenter
. (It's possible to create a type-safe version of this, but it requires dependent types, which is a quite complex type feature that Swift doesn't have.)
OK, so maybe you really want this central hub and who needs type safety? Why not? You just have to say what you mean, which is that any handler has to be able to accept any request, even if it doesn't act on it. The compiler can't prove anything more specific than that because at compile time it doesn't know the types. So fine, as?
it to death.
protocol Request {}
protocol Handler {
func canHandle(_ request: Request) -> Bool
func handle(_ request: Request)
}
class RequestProcessor {
private var handlers: [Handler] =
func register(_ handler: Handler) {
handlers.append(handler)
}
func handle(_ request: Request) {
for handler in handlers where handler.canHandle(request) {
handler.handle(request)
}
}
}
class LoginHandler: Handler {
func canHandle(_ request: Request) -> Bool {
return request is LoginRequest
}
func handle(_ request: Request) {
guard let loginRequest = request as? LoginRequest else { return }
// handle loginRequest
}
}
But I'd almost certainly get rid of the Mediator pattern. If the goal is to swap in and out processors for testing or whatnot, I'd just use typical dependency injection techniques. Pass the LoginHandler
to whatever method creates the LoginRequest
.
Thanks for this detailed answer, I appreciate the push away from potentially going down completely the wrong path. The reasoning is purely academic, I'm not trying to solve a specific problem, rather trying to port the .NET Mediatr library to Swift, with a bit of learning thrown in for good measure. I think I can live with a variation of the last example you gave but pulling theguard
/as?
clauses into the processor and have the handlers be explicit about the request they handle, probably with a base class rather than a protocol.
– Steve Wilford
Jan 3 at 16:19
I think you'll find you can't move theas?
clause to the caller, because anyHandler
must implement somehandle
method that accepts the same type. You can't call a method using a type that isn't known at compile-time. While an interesting problem, I would recommend against porting a framework that solves a C# problem (likely driven by the pre-3.0 lack of first-class functions), and instead reimagine the underlying problem using Swift tools (particularly first-class functions), and focusing on Swift problems. Most "patterns" are not nearly as universal as they claim to be.
– Rob Napier
Jan 3 at 16:29
add a comment |
First, the standard advice:
I am trying to implement a general-purpose Mediator pattern in Swift
Don't. Start with the actual problem you're trying to solve, and design good and necessary abstractions for that problem. Don't create generic things just to be generic. Swift will bite you over and over again. Even stdlib, which really needs super-generic things, often has to step outside of pure Swift to pull it off (using compiler special knowledge, and gyb templating). "Being generic" is not a goal in itself. You're almost certainly making this too complicated. Everyone does.
OK, that's out of the way. Second piece of advice: This is not a good use of a protocol with associated types (PAT). The point of a PAT is to add methods to types, not to be types. You never pass Collection
itself or store things of that "type." You create methods that can generically work on any type that is a Collection
. There is no such type as [Collection]
.
The fundamental problem with your approach is that there's no way to implement RequestProcessor.process()
without resorting to as?
casting, which breaks the point of type safety. How does processor
know to call LoginHandler.process
? Why that one? What if two different handlers accepted LoginRequest
? What if no handler accepts that type?
What you've designed here isn't the Mediator pattern. The Mediator pattern joins together colleagues who share a single interface, so it would look like this:
class RequestProcessor<Request> {
var handlers: [(Request) -> Void] =
func register(handler: @escaping (Request) -> Void) {
handlers.append(handler)
}
func handle(request: Request) {
for handler in handlers {
handler(request)
}
}
}
And you'd have a RequestProcessor
for each kind of request, not a generic "processor of every kind of request." Creating a generic one necessarily (in Swift) removes type safety, in which case you're basically creating a slightly Swiftier NotificationCenter
. (It's possible to create a type-safe version of this, but it requires dependent types, which is a quite complex type feature that Swift doesn't have.)
OK, so maybe you really want this central hub and who needs type safety? Why not? You just have to say what you mean, which is that any handler has to be able to accept any request, even if it doesn't act on it. The compiler can't prove anything more specific than that because at compile time it doesn't know the types. So fine, as?
it to death.
protocol Request {}
protocol Handler {
func canHandle(_ request: Request) -> Bool
func handle(_ request: Request)
}
class RequestProcessor {
private var handlers: [Handler] =
func register(_ handler: Handler) {
handlers.append(handler)
}
func handle(_ request: Request) {
for handler in handlers where handler.canHandle(request) {
handler.handle(request)
}
}
}
class LoginHandler: Handler {
func canHandle(_ request: Request) -> Bool {
return request is LoginRequest
}
func handle(_ request: Request) {
guard let loginRequest = request as? LoginRequest else { return }
// handle loginRequest
}
}
But I'd almost certainly get rid of the Mediator pattern. If the goal is to swap in and out processors for testing or whatnot, I'd just use typical dependency injection techniques. Pass the LoginHandler
to whatever method creates the LoginRequest
.
First, the standard advice:
I am trying to implement a general-purpose Mediator pattern in Swift
Don't. Start with the actual problem you're trying to solve, and design good and necessary abstractions for that problem. Don't create generic things just to be generic. Swift will bite you over and over again. Even stdlib, which really needs super-generic things, often has to step outside of pure Swift to pull it off (using compiler special knowledge, and gyb templating). "Being generic" is not a goal in itself. You're almost certainly making this too complicated. Everyone does.
OK, that's out of the way. Second piece of advice: This is not a good use of a protocol with associated types (PAT). The point of a PAT is to add methods to types, not to be types. You never pass Collection
itself or store things of that "type." You create methods that can generically work on any type that is a Collection
. There is no such type as [Collection]
.
The fundamental problem with your approach is that there's no way to implement RequestProcessor.process()
without resorting to as?
casting, which breaks the point of type safety. How does processor
know to call LoginHandler.process
? Why that one? What if two different handlers accepted LoginRequest
? What if no handler accepts that type?
What you've designed here isn't the Mediator pattern. The Mediator pattern joins together colleagues who share a single interface, so it would look like this:
class RequestProcessor<Request> {
var handlers: [(Request) -> Void] =
func register(handler: @escaping (Request) -> Void) {
handlers.append(handler)
}
func handle(request: Request) {
for handler in handlers {
handler(request)
}
}
}
And you'd have a RequestProcessor
for each kind of request, not a generic "processor of every kind of request." Creating a generic one necessarily (in Swift) removes type safety, in which case you're basically creating a slightly Swiftier NotificationCenter
. (It's possible to create a type-safe version of this, but it requires dependent types, which is a quite complex type feature that Swift doesn't have.)
OK, so maybe you really want this central hub and who needs type safety? Why not? You just have to say what you mean, which is that any handler has to be able to accept any request, even if it doesn't act on it. The compiler can't prove anything more specific than that because at compile time it doesn't know the types. So fine, as?
it to death.
protocol Request {}
protocol Handler {
func canHandle(_ request: Request) -> Bool
func handle(_ request: Request)
}
class RequestProcessor {
private var handlers: [Handler] =
func register(_ handler: Handler) {
handlers.append(handler)
}
func handle(_ request: Request) {
for handler in handlers where handler.canHandle(request) {
handler.handle(request)
}
}
}
class LoginHandler: Handler {
func canHandle(_ request: Request) -> Bool {
return request is LoginRequest
}
func handle(_ request: Request) {
guard let loginRequest = request as? LoginRequest else { return }
// handle loginRequest
}
}
But I'd almost certainly get rid of the Mediator pattern. If the goal is to swap in and out processors for testing or whatnot, I'd just use typical dependency injection techniques. Pass the LoginHandler
to whatever method creates the LoginRequest
.
answered Jan 3 at 14:30
Rob NapierRob Napier
207k28306434
207k28306434
Thanks for this detailed answer, I appreciate the push away from potentially going down completely the wrong path. The reasoning is purely academic, I'm not trying to solve a specific problem, rather trying to port the .NET Mediatr library to Swift, with a bit of learning thrown in for good measure. I think I can live with a variation of the last example you gave but pulling theguard
/as?
clauses into the processor and have the handlers be explicit about the request they handle, probably with a base class rather than a protocol.
– Steve Wilford
Jan 3 at 16:19
I think you'll find you can't move theas?
clause to the caller, because anyHandler
must implement somehandle
method that accepts the same type. You can't call a method using a type that isn't known at compile-time. While an interesting problem, I would recommend against porting a framework that solves a C# problem (likely driven by the pre-3.0 lack of first-class functions), and instead reimagine the underlying problem using Swift tools (particularly first-class functions), and focusing on Swift problems. Most "patterns" are not nearly as universal as they claim to be.
– Rob Napier
Jan 3 at 16:29
add a comment |
Thanks for this detailed answer, I appreciate the push away from potentially going down completely the wrong path. The reasoning is purely academic, I'm not trying to solve a specific problem, rather trying to port the .NET Mediatr library to Swift, with a bit of learning thrown in for good measure. I think I can live with a variation of the last example you gave but pulling theguard
/as?
clauses into the processor and have the handlers be explicit about the request they handle, probably with a base class rather than a protocol.
– Steve Wilford
Jan 3 at 16:19
I think you'll find you can't move theas?
clause to the caller, because anyHandler
must implement somehandle
method that accepts the same type. You can't call a method using a type that isn't known at compile-time. While an interesting problem, I would recommend against porting a framework that solves a C# problem (likely driven by the pre-3.0 lack of first-class functions), and instead reimagine the underlying problem using Swift tools (particularly first-class functions), and focusing on Swift problems. Most "patterns" are not nearly as universal as they claim to be.
– Rob Napier
Jan 3 at 16:29
Thanks for this detailed answer, I appreciate the push away from potentially going down completely the wrong path. The reasoning is purely academic, I'm not trying to solve a specific problem, rather trying to port the .NET Mediatr library to Swift, with a bit of learning thrown in for good measure. I think I can live with a variation of the last example you gave but pulling the
guard
/ as?
clauses into the processor and have the handlers be explicit about the request they handle, probably with a base class rather than a protocol.– Steve Wilford
Jan 3 at 16:19
Thanks for this detailed answer, I appreciate the push away from potentially going down completely the wrong path. The reasoning is purely academic, I'm not trying to solve a specific problem, rather trying to port the .NET Mediatr library to Swift, with a bit of learning thrown in for good measure. I think I can live with a variation of the last example you gave but pulling the
guard
/ as?
clauses into the processor and have the handlers be explicit about the request they handle, probably with a base class rather than a protocol.– Steve Wilford
Jan 3 at 16:19
I think you'll find you can't move the
as?
clause to the caller, because any Handler
must implement some handle
method that accepts the same type. You can't call a method using a type that isn't known at compile-time. While an interesting problem, I would recommend against porting a framework that solves a C# problem (likely driven by the pre-3.0 lack of first-class functions), and instead reimagine the underlying problem using Swift tools (particularly first-class functions), and focusing on Swift problems. Most "patterns" are not nearly as universal as they claim to be.– Rob Napier
Jan 3 at 16:29
I think you'll find you can't move the
as?
clause to the caller, because any Handler
must implement some handle
method that accepts the same type. You can't call a method using a type that isn't known at compile-time. While an interesting problem, I would recommend against porting a framework that solves a C# problem (likely driven by the pre-3.0 lack of first-class functions), and instead reimagine the underlying problem using Swift tools (particularly first-class functions), and focusing on Swift problems. Most "patterns" are not nearly as universal as they claim to be.– Rob Napier
Jan 3 at 16:29
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54022102%2fswift-mediator-pattern-collection-of-generic-protocols%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
I would remove
associatedType
constraint fromHandler
so it will beprotocol Handler { func handle(_ request: Request) }
. Now we can declare anarray
ofHandlers
insideRequestProcessor
.– Kamran
Jan 3 at 13:33