There is no silver bullet to such a problem. By its very nature you can find many solutions and the suitability of each solution will depend on how the rest of the code is structured and the functions it performs.
There are several ways to proceed and what follows is just one technique that I have used before myself when faced with a very similar problem. For the sake of brevity, I will constantly refer to your code as application but it can be a service or a library too.
Prerequisites
Before you proceed make sure you have the done the following:
Understand what your application is supposed to do. This goes well beyond just the requirements specification. The more you understand the finer points of your application's behaviour the better off you will be. Also, do not fear to challenge the spec (if you have a direct line to your users). You may find that sometimes people don't really care about a particular functionality/behaviour that you thought was mandatory. Sometimes you may even find functionality/behaviour that people hate. This extends not just to human users but also to system users (e.g., programs using your API). However, gather consensus before proceeding with this approach.
Start writing deterministic tests for your application. You should aim to cover at least the broad "specs". Once you have these tests written you can refactor easily. Tests that are written as code should be preferred (e.g., integration/functional tests). However, if you cannot code test cases then document the manual testing procedure so that you (or even better someone else) can replicate it in an error free way.
Start a log book. Your SCM can help you track changes and recover a previous version. So make changes in small increments and test them. Once you and an independent tester both are sure your changes are good then check-in and move on to the next bit. Good change descriptions and notes in your log book can help you follow your tracks in case something bad happens in the future. If others join your effort they can study your notes to read your mind.
Ok, what now?
Once you are familiar with the application and have the test cases ready you should try to attack the usages of your BadClass
. Try to replace this class's access by a better pattern. For example, if your class's members are being accessed by another class try to see if the callee instead should have that member available as a class field or method parameter. See if instead of making these members visible the BadClass
can provide a trait/function to be used --try to see if that trait should be placed in a different class altogether. In general, you are replacing the usage with a better pattern instead of trying to fix the BadClass
.
If you know your application or domain well then you can perhaps even figure out if the usage is required or not (may be you can just chuck away that functionality or replace it with something better).
When attempting this approach, avoid trying to solve too many problems at once. Aim for smaller improvements first (low hanging fruits). See if that simplifies the problem to an extent where you can introduce a better pattern.
Once the BadClass
is no longer referenced, you can chuck it away.