Can someone explain in simple terms the "signals and slots" pattern?
Can someone explain in simple terms the "signals and slots" pattern?
Signals and slots are a way of decoupling a sender (the signal) and zero or more receivers (the slots). Let's say you a system which has events that you want to make available to any other part of the system interested in those events. Rather than hard-wiring the code that generates event to the code that wants to know about those events, you would use a signals and slots pattern.
When the sender signals an event (usually by calling the function associated with that event/signal) all the receivers for that event are automatically called. This allows you to connect and disconnect receivers as necessary during the lifetime of the program.
Since this question was tagged C++, here is a link to the Boost.Signals library which has a much more thorough explanation.
I think one can describe signals and slots best when you are looking at them as a possible implementation vehicle for the Observer Pattern or Publish/Subscriber Pattern. There is one
signal, for example
buttonPressed(IdType) on the Publisher Side. Whenever the button is pressed, all slots that are connected to that signal are called. Slots are on the Subscriber Side. A slot could for example be
Along with the event "button pressed", the slot would know which button was pressed, since the id would have been handed over.
IdType represents the type of the data sent over the connection between the Publisher and the Subscriber. An operation possible for the Subscriber would be
connect(signal, slot) which could connect
sendMail(IdType), so that if the button is pressed, that particular slot is called.
The good thing about this is that the subscriber (the slot side) doesn't need to care about details of the signal. It just needs to connect. Thus, here we have a great deal of loose coupling. You can change the buttons implementation, but the interface for the slots would still be the same.
Look at Qt Signals/Slots or Boost Signals for more informations.
Imagine having a GUI in your application. Most of the time, control flow wouldn't be very linear, i.e. instead of having a clear sequence of actions you'd have a user that interacts with the GUI (like buttons, menus etc.).
This is essentially an event driven model which can be implemented quite nicely with the signals and slots pattern. signals are events which are generated by objects (think GUI components) and slots are the receivers of those events.
Here is an example: imagine you have a checkbox, represented as an object in your programming language. Multiple things can happen to that checkbox: it can be toggled, which in turn also means that it's either set or unset. Those are the signals which it can emit. We will name them checkboxToggled, checkboxSet and checkboxUnset. As you see, in this example, the checkbox will always emit the checkboxToggled signal when toggled, but also exactly one of the two other signals, depending on how the state changes.
Now imagine having some other objects, namely a label, which for the sake of this example always exists as an object but can "appear" and "disappear" and a system beep (also represented by an object), which can simply beep. Those are the slots those objects have. We will call them "messageAppear", "messageDisappear" and "beep".
Suppose you want the system beep to beep everytime the checkbox is toggled, and the label to appear or disappear depending on whether the user checked or cleared the checkbox.
You would thus connect the following signals to the following slots (signals on the left, slots on the right):
checkboxToggled -> beep checkboxSet -> messageAppear checkboxUnset -> messageDisappear
That's basically it.
signals and slots may also have arguments. For example, using a slider which sets a numerical value, you would like to send the changed value along with the emitted signal as soon as the user moved the slider: sliderChanged(int).
Of course, to actually do something useful you would write some own classes which would contain some own signals and slots. This is done quite easily and using these own signals and slots, you have a nice way to interact with the GUI or other parts of your code in an event driven manner.
Keep in mind that signals and slots are often symmetric in the sense that there may often be a signal corresponding to a slot. For example, a checkbox may emit a signal when toggled, but it may also contain a slot that toggles the checkbox itself. It would be easy to implement to separate checkboxes that are always set oppositely to each other.
I'm assuming you're talking about QT's signals and slots.
It's very simple.
An instance of a class can fire a signal and another instance of perhaps another class can catch that signal in a slot. It's sort of like a function call only that the guy that calls the function doesn't need to know who wants to receive the call.
The best way to illustrate is with an example.
The class QPushButton has a signal QPushButton::clicked(). That signal is fired whenever the button is clicked. The push button doesn't need to know who's interested to know that a click occurred. it just fires the signal and whoever's interested can connect to it.
The QDialog in which the button is placed in is infact interested to know when the button was clicked. It has the slot MyDialog::buttonClicked(). On MyDialog c'tor you need to connect() the buttons click() signal to the dialog's buttonClicked() slot so that the slot will be called when the signal is fired.
A bunch of more advanced stuff:
Here's more information in QT's documentation.
The best example and explanation I've found for signals and slots is this code project article.
There is a common misunderstanding that classes are nouns like Person, Dog, Bicycle and such. Then it makes sense to think that a person (instance) has a dog and a bicycle.
Let's start with what objects are (supposed to be). Objects are data and procedures. What are programs? Data and procedures. Objects are supposed to be (relatively) "small" independent sub-programs. Because oo programming is taught very vaguely and misused (citation needed), people think everything needs to be a class or an object. This is not so, objects are "small" independent programs with a "small" API (public subroutines). Some programmers don't even break their project into sub-programs and simply use objects where data and procedures are more suitable.
Now, assuming that we agree that objects are programs, we may agree that in most cases, programs don't need to have copies of other programs of a similar size and complexity (i.e an object does not a pointer to another object), it may need smaller programs for managing data (like data-structures) but imho does not need another object.
Why? Because coupling objects makes them dependent. Why is that bad? Because when objects are independent you can test them and also promise other programmers and clients that the object (a small independent program) is capable of performing certain tasks with high certainty. You can also be sure that it continues to perform as long as no changes were made to that object.
So what are slots and signals? If you understand that objects are like programs and they should not ideally hold copies or pointers to other objects than you need some way for them to communicate. For instance, processes that run on your computer can use sockets, IP addresses and ports to communicate. Objects can use something very similar to RPC called signals and slots. These are a data-structure intended as an intermediary between two larger objects that store object's subroutines (
slots) and allow other objects to call (
signal) these subrutines (
slots) with suitable parameters without knowing anything about these other objects other than which parameters they require.
So the underlying structure are sets (possibly arrays) of (possibly) strongly typed procedure pointers, that other objects can call with suitable parameters without a pointer to these objects. The callers only need access to the signal object instead (which holds no implementation details) that defines the expected parameters.
This is also flexible because it allows some special use cases like slots which only respond to the signal once, multiple slots for one signal and other similar use cases like debouncing.