You could try using a Model-View-Controller (MVC) pattern.
1. Model
The classes involved in creating the actual maze-y traffic simulation grid, with the information about where the intersections and cars are. Including the Car, Grid, and Intersection types.
2. View
One or more classes capable of describing the model in a format suitable for interaction by a controller. Like a WindshieldView (the intersections ahead of a designated car's position). Another View could be a TrafficDispatcherView which detects heavily congested areas on the map.
3. Controller
A class that invokes changes to the model. For instance, the Driver who uses the WindshieldView class to respond to events (reaching an intersection) to make decisions (taking a turn). Another Controller could be a class that spawns new cars periodically or puts up detours temporarily.
The MVC pattern is event-driven so you would have the model publish events (like OnCarReachedIntersection) for the controller Driver to subscribe to. When the model fires the event, it constructs a pertinent view and passes it along as an event argument. As another example, a driver with a radio can also get information from the TrafficDispatcher by subscribing to the OnRadioAnnouncement event, which tells it what areas of the grid are congested.