Smell Detail

Global Data

全局数据

Couplers

Global data can be modified from anywhere in the codebase, and there's no mechanism to detect which code made the modifications.

Define

Introduction

Global data is one of the most pungent code smells. The problem with global data is that it can be modified from any corner of the codebase, and there's no mechanism to detect which code made the modifications. Time and again, global data causes mysterious bugs where the source of the problem is somewhere far away, making it extremely difficult to find the erroneous code.

Key Identifiers

  • Global variables accessible from anywhere in the codebase
  • Static class variables that can be modified globally
  • Singleton objects with mutable state
  • Data that can be modified without clear ownership or control
  • Lack of encapsulation around shared data

Refactoring Suggestions

  1. Encapsulate Variable: Wrap global data with functions to control access
  2. Move Field: Move global data into a class or module where it belongs
  3. Limit Scope: Restrict access to data within specific modules or classes
  4. Make Immutable: If data must be global, make it immutable after initialization
  5. Dependency Injection: Pass data as parameters rather than accessing globally

Explanation

Global data violates the principle of encapsulation and makes code unpredictable. When data can be modified from anywhere, it becomes impossible to reason about the state of the system. The primary defense is to encapsulate the data with functions, allowing you to see where it's being modified and control access to it. Ideally, move the data into a class or module and allow only that module's code to use it, thus limiting its scope as much as possible.

Code Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import java.util.*;
// Typical example of global data
class GlobalConfig {
// Global variables - can be accessed and modified from anywhere
public static String DATABASE_URL = "localhost:3306";
public static int MAX_CONNECTIONS = 100;
public static boolean DEBUG_MODE = false;
public static Map<String, String> CACHE = new HashMap<>();
public static List<String> ERROR_MESSAGES = new ArrayList<>();
}
// Singleton pattern can also cause global data problems
class UserManager {
private static UserManager instance;
public static List<String> currentUsers = new ArrayList<>(); // Global mutable state
public static int totalLogins = 0;
public static UserManager getInstance() {
if (instance == null) {
instance = new UserManager();
}
return instance;
}
public void addUser(String username) {
currentUsers.add(username);
totalLogins++; // Global state is modified
}
}
// Classes using global data - the problem is that it can be modified anywhere
class DatabaseService {
public void connect() {
// Direct access to global variables
String url = GlobalConfig.DATABASE_URL;
int maxConn = GlobalConfig.MAX_CONNECTIONS;
// Modify global state
GlobalConfig.CACHE.put("last_connection", new Date().toString());
if (GlobalConfig.DEBUG_MODE) {
System.out.println("Connecting to: " + url);
}
}
public void logError(String error) {
// Global error list is modified
GlobalConfig.ERROR_MESSAGES.add(error);
}
}
class UserService {
public void login(String username) {
// Modify global data
UserManager.getInstance().addUser(username);
// Direct modification of global state
UserManager.totalLogins++;
UserManager.currentUsers.add(username + "_session");
}
public void processUser() {
// Depend on global state
if (UserManager.totalLogins > 1000) {
GlobalConfig.DEBUG_MODE = true; // Modify global state here
}
// Modify global cache
GlobalConfig.CACHE.put("last_user_count",
String.valueOf(UserManager.currentUsers.size()));
}
}
// Another class also modifying the same global data
class AdminService {
public void resetSystem() {
// Clear global data
GlobalConfig.CACHE.clear();
GlobalConfig.ERROR_MESSAGES.clear();
UserManager.currentUsers.clear();
UserManager.totalLogins = 0;
// Modify configuration
GlobalConfig.MAX_CONNECTIONS = 50;
GlobalConfig.DEBUG_MODE = false;
}
public void updateConfig(String dbUrl) {
// Modify global configuration from another place
GlobalConfig.DATABASE_URL = dbUrl;
GlobalConfig.CACHE.put("config_updated", "true");
}
}