The first step is to find the classes that the program needs. This is more difficult than identifying the primary function of a program. You cannot simply perform a procedural decomposition of the problem, take the resulting structure types or data structures, and make them into classes. Your classes must be the central, active entities in your program.
One technique for identifying classes is to write a description of the program's purpose, list all the nouns that appear in the description, and choose your classes from that list. This is a simplistic approach whose success depends on how well the original description is written, but it can help give you ideas if you are new to object-oriented design.
It's easiest to identify classes for a program that models physical objects. For example, if your program handles airline seating reservations, you will probably need an Airplane class and a Passenger class. If your program is an operating system, a Device class for representing disk drives and printers is a likely candidate class.
However, many programs don't model physical entities. For these situations, you must identify the conceptual entities that the program manipulates. Sometimes these are readily identifiable: a Rectangle class and a Circle class are obvious candidates for a graphic drawing program. In other cases, these are not as easy to visualize. For example, a compiler might need a SyntaxTree class, and an operating system might need a Process class.
Less obvious candidates for classes are events (things that happen to an object) and interactions (things that happen between objects). For example, a Transaction class could represent things like loans, deposits, or funds transfers in a bank program. A Command class could represent actions performed by the user in a program.
You may see possible hierarchies for your classes. If you've identified BinaryFile and TextFile as candidate classes, you might derive them from a base class called File. However, it is not always obvious when a hierarchy is appropriate. For instance, a bank program could use a single Transaction class, or it could use separate Loan, Deposit, and Transfer classes derived from Transaction. As with the classes themselves, any hierarchies identified at this stage are simply candidates to be refined or discarded later in the design process.
All of the above candidate classes are meant to model elements in the problem you're trying to solve. Your program may also need another category of candidate classes: those that can be used to implement the other classes you've found. For instance, you may have identified a class that can be implemented using the SortedList class that you previously wrote. In that case, SortedList becomes a candidate class, even if your program description didn't explicitly mention sorted lists. In general, it is too early to think about how each class should be implemented, but it is appropriate to find ways to build classes using existing class libraries.