To effectively debounce an input, one must decide how the debounced logic should interpret various signals (e.g. what must the input signal do to be regarded as a "high", and what must it then do to be considered a "low"). In many cases, if precise timing isn't required, the simplest way to debounce a push button is to simply sample it periodically. If the sampling interval exceeds the longest bounce time, but the duration of every push, minus its bounce time, exceeds the sampling interval, then every button push will register exactly once.
If more precise input timing is needed, another useful approach is to have an input transition trigger a timing circuit, and ignore the state of the input until the timer expires (one may if desired ignore the input for different lengths of time following a press and release). This may in many cases be done fairly conveniently in software, even if the input is wired to an interrupt pin (the pin's interrupt service routine disables the pin's interrupt, but sets up a timer whose interrupt service routine will re-enable the pin's interrupt).
Another hardware approach if one has both normally-open and normally-closed contacts available, is shown here. There are quite a few variations, based upon whether one wishes to run +5 and ground out to the switches and have one return wire, or whether one would rather run ground only and have two return wires, or use switch-mounted diodes and have two wires total. These approaches can be especially advantageous when interfacing to low-power circuitry switches that may stay for a long time in each state, since current only has to flow through the switch when it changes state. Further, they are essentially immune to even the most horrible switch-bounce conditions imaginable (it's theoretically possible that just the 'right' pattern of switch bounce could trigger metastability, but the probability of that happening is extremely low). The only disadvantage is the requirement that the switch have both normally-open and normally-closed contacts.