0. MAKE A CONCEPTUAL MODEL OF THE PROBLEM-DOMAIN THAT YOUR PROGRAM WILL REPRESENT AND WORK ON.
Jot down a circles-and-arrows model (diagram) of the types of entities that exist (and are important as far as your program will be concerned) in your problem domain. The circles, with an entity-type-name written in each, represent the important different kinds of objects/entities in your domain. The arrows, which you may refer to later when defining attributes or functions that work on the entity types, summarize the important relationships you have noticed between the different kinds of entities in your problem-domain. Look around for groups of entity-types in your domain model which are really just different subtypes of a common kind of general entity type in your domain. Create a named circle for the general type of entity, drawing it above the group of more specific subtype entity-type circles, and join the general-entity-type circle, to each of the entity-subtype circles separately, with a different kind/colour of arrow than you used to represent relationships between one kind of entity and a completely different kind in your domain model diagram.
1. TURN THE CONCEPTUAL MODEL OF THE DOMAIN INTO A STRUCT-BASED DATA MODEL
Organize data (variable) definitions in the program you are writing into "struct" definitions, where each kind of struct has a set of attributes that together represent the essential properties of some kind of entity in your problem domain.
(And, for advanced credit, create an additional named struct-type to represent the properties of some kind of abstract record-keeping entity-type you are concocting as part of your "solution" domain. A "solution" domain model is an extension of your model of the problem domain, where you are adding abstractions (new variables) into your problem domain to create a computer model of the solution to whatever problem you've been asked to program a solution for in the problem domain. Some of those solution-domain entity types may not have occurred to you when you first looked around at the external "outside of the program" problem-domain to create your struct-definition-based data model of the problem domain entity types.)
2. NAME YOUR DATA TYPES AFTER THE PRECISE NAMES OF DOMAIN ENTITY TYPES
Use the common (but precise) name of each kind of domain entity as the type-name of the corresponding struct definition.
3. METHODS - are functions/procedures specifically applicable to the attributes of a single struct type.
For each type of struct you have defined, define the interface signature of, and code for the implemention of, a set of functions which access the attributes of, set the attribute values of, or compute some function of the attributes of a single type of struct.
4. INHERITANCE
Object-oriented languages let you create a struct-type which is meant to represent a specific subtype of domain entity, whenever you have already created a struct-type (and its functions) to represent the common attributes shared by several subtypes of entity. That is, you have already created an abstract supertype struct definition to represent general properties of a general category of domain entity, now you want to add attributes (or specific values of attributes) that describe how different subtypes of the general entity differ from each other.
In an object oriented programming language, the subtype of struct can be created so that its definition references (mentions) the supertype struct type by name.
Then any in-memory instance of that subtype struct inherits all the attributes and applicable functions of the supertype struct definition. Then you add more, specific attributes, attribute value settings, and function interfaces or function implementations to the new subtype of struct you are creating.
5. PROGRAM WITH YOUR DOMAIN-ENTITY-MODELLING STRUCTS AND THEIR STRUCT-TYPE-SPECIFIC FUNCTION-SETS
To represent as program data the state of the problem-domain or the values from your programmatic manipulations of it or calculations about it, allocate in-memory instances of the appropriate struct types, and set or read their attribute values. To access or manipulate attribute values of a struct instance (also called an object), use ONLY the functions you defined to apply to that type of struct. Do not access or assign to the struct attributes without going through one of the functions you defined. This discipline has several benefits, including allowing you to safely modify some details of each struct type's representation or state-maintenance logic without having to modify external program sections that refer to or set the attributes of the struct.
Some advantages of all this are:
A. The complexity of your program, as a representation of your problem domain, will be minimized by directly modelling, one-to-one, the obvious entity types that occur in that domain, as opposed to just creating a large set of individual, ungrouped variables to represent individual attributes of things in the domain.
B. Provided you do not go crazy creating super-abstract, impenetrable-to-understand supertype struct types, the creation of supertype struct types and supertype function definitions further reduces the complexity of your program, by representing common properties of, and general manipulations of the domain (model) only once in the program. Lack of repeated similar data-representations and code sections leads to a smaller program, and assists in program incremental development or maintenance, because it prevents error-prone and program-complicating partial-modifications where some but not all of the similar data definitions and code sections are modified.
C. The set of functions grouped to be applicable to each type of domain entity form a modular, loosely coupled program structure.
D. By restricting access to the domain model and modification of it to predefined and grouped functions only, you create a program that is easier and less risky to modify, Program modifications generally have fewer cascading dependent modifications and fewer dangerously randomly located sets of co-dependent modifications.