Use uniform buffers (ie constant buffers in D3D lingo).
So long as all your shaders agree on the layout and binding point of each such buffer then updating becomes a breeze. Models have no need to know anything about shaders at all. They need only update the model-view matrix in their constant buffer and the rendering pipeline will use it automatically.
At a minimum I would argue you should have two such buffers. The first should store your projection matrix, camera matrix, the concatenated camera-projection matrix, viewport information, frustrum details, and matrix inverses. You only need to update this buffer once per scene.
Then give each model another buffer to store its model-view matrix, normal matrix, inverses, and material properties. This is updated once for each model and can be done in a different update pass (if/when appropriate). The material information can/should be moved to a third material-specific buffer if you have the ability to share materials between multiple objects.
In a forward-shading setup it makes some sense to put all the lights in another buffer and in deferred-shading it likewise makes sense to use a per-light buffer for the light pass (in place of a model/material buffer used in the geometry pass).
Note that you need a moderately up-to-date version of GL to use uniform buffers at all (3.1 or an extension; common enough today except on some older but still-in-service laptops) and you need a fairly recent version to be able to bind uniform buffers to specific binding locations from inside the shader code (4.2; still uncommon but getting better) otherwise you have to do more work in your CPU-side code to set things up (your CPU code needs to know the right binding points anyway, so it's more of an API smell than a serious issue). OpenGL|ES didn't add uniform buffers until 3.0 which is still unsupported on most popular mobile platforms, unfortunately.
If buffers aren't an option then you will need some global place to store index locations for the active shader. You can use
glGetUniformLocation after loading your shader to find the indices for well-known names (like
ModelViewMatrix or the like) then store these indices. Your render can map enum values like
MODEL_VIEW passed to a
SetUniform wrapper function to look into the bound shader, find the index, and call
glUniform properly. It's not a particular huge change in client code usage from buffers other than needing to set each uniform individually if you get it all wrapped up nicely.
See GLSL Inteface Blocks and Uniform Buffer Objects.