Introduction: What Is Lack of Cohesion in Methods 96B?
Software maintainability isn’t just about writing code that works—it’s about crafting code that developers can understand, modify, and extend with confidence. One of the most respected metrics for assessing code quality is the Lack of Cohesion in Methods (LCOM), and specifically, the LCOM 96B variant, which was proposed by Henderson-Sellers in 1996. This metric doesn’t just measure how much your methods are related—it exposes the hidden pain points that can cripple maintainability and balloon technical debt.
LCOM 96B helps developers and teams identify classes that do too much, or that have methods operating on disjoint sets of fields. Understanding this metric can be a game-changer for code reviews, refactoring efforts, and onboarding new team members. By quantifying cohesion, teams gain actionable insights into their object-oriented design, ensuring each class serves a clear and single responsibility.
The Fundamentals: How LCOM 96B Works
LCOM 96B is calculated using the following formula:
LCOM = (1 - (Σ(mA) / (m * a)))
Where:
m
= number of methods in the classa
= number of attributes (fields/variables) in the classΣ(mA)
= sum, for each attribute, of the number of methods that access that attribute
The closer the LCOM value is to 0, the more cohesive the class; higher values indicate lower cohesion. This metric highlights classes where methods do not share common data, suggesting a violation of the Single Responsibility Principle.
Let’s look at a simple example in Python:
class ExampleA:
def __init__(self):
self.x = 0
self.y = 0
def increment_x(self):
self.x += 1
def increment_y(self):
self.y += 1
Here, each method operates on a different field—an LCOM 96B analysis would reveal low cohesion.
Deep Dive: Calculating and Interpreting LCOM 96B with Code Examples
Let’s walk through the calculation for a more complex TypeScript class:
class ShoppingCart {
private items: string[] = [];
private total: number = 0;
addItem(item: string, price: number) {
this.items.push(item);
this.total += price;
}
getTotal() {
return this.total;
}
clearCart() {
this.items = [];
this.total = 0;
}
}
- Methods: 3 (
addItem
,getTotal
,clearCart
) - Attributes: 2 (
items
,total
) addItem
accesses both,getTotal
accessestotal
,clearCart
accesses both.
Calculating Σ(mA):
items
is accessed byaddItem
,clearCart
→ 2total
is accessed by all three methods → 3
Σ(mA) = 2 + 3 = 5
LCOM = 1 - (5 / (3 * 2)) = 1 - (5/6) ≈ 0.167
A low LCOM indicates strong cohesion—methods interact closely with attributes. Cohesive classes are easier to maintain, extend, and test.
Why Cohesion Matters: Real-World Impact and Anti-Patterns
High cohesion (low LCOM) means a class has a well-defined responsibility and its methods work together using shared data. In contrast, low cohesion (high LCOM) often flags "god classes" or classes that try to do too much, making them harder to understand and maintain. This anti-pattern leads to increased bugs, more challenging testing, and greater risk when making changes.
Consider a JavaScript example of poor cohesion:
class UserProfile {
constructor() {
this.name = '';
this.email = '';
this.cart = [];
}
updateName(newName) {
this.name = newName;
}
addToCart(item) {
this.cart.push(item);
}
}
updateName
and addToCart
target unrelated fields, signaling that perhaps UserProfile
should be split into two classes.
Strategies to Improve Class Cohesion
Improving class cohesion is a proactive process that can dramatically enhance the maintainability and clarity of your codebase. Start by regularly analyzing your classes with LCOM 96B or similar metrics, both during development sprints and as part of your code review checklist. Automated static analysis tools can help flag problematic classes early, enabling teams to address low cohesion before it snowballs into technical debt. However, metrics alone are not enough; always pair them with peer reviews and architectural discussions to understand the intent behind code structure and ensure that refactoring aligns with your project goals.
One of the most effective strategies for increasing cohesion is adhering to the Single Responsibility Principle (SRP). SRP dictates that a class should have one, and only one, reason to change—meaning its methods should focus on a single area of functionality. When you notice a class with disparate groups of methods manipulating unrelated fields, consider extracting those methods into new, focused classes with clear, singular responsibilities. This not only improves cohesion but also facilitates easier testing and reuse.
Another practical approach involves grouping related methods and fields together during the design phase. Before writing a class, sketch out its responsibilities and the data it should manage. If you find yourself adding methods that operate on only a subset of fields, pause and evaluate whether those behaviors truly belong in the same class. Using domain-driven design concepts can help clarify boundaries and lead to more natural, cohesive class structures.
Refactoring is your ally in maintaining high cohesion as codebases evolve. Regularly revisit your classes and look for opportunities to split overly broad classes into smaller units. Don’t be afraid to create new abstractions or utility classes if it means each class better encapsulates a single concept. Many modern IDEs and code editors offer refactoring tools that make it easier to extract classes or methods safely and efficiently.
Finally, foster a team culture where code cohesion is valued. Encourage developers to leave comments or documentation explaining why a class’s methods and fields belong together. Conduct knowledge-sharing sessions to review examples of both good and bad cohesion from your codebase, helping everyone internalize best practices. Remember, cohesion is not just a technical metric—it’s a mindset that leads to better software architecture and happier teams.
JavaScript Function: Parsing Codebase and Calculating LCOM 96B
Automating the calculation of LCOM 96B can be a powerful addition to your refactoring toolkit. Below is a JavaScript function that, given a string containing the source code of a single class, parses the class to identify its methods and fields, then computes the LCOM 96B score. This example is simplified for educational purposes and works best on straightforward ES6 classes. For more complex scenarios, integrating with a parser like Babel or Acorn is recommended.
/**
* Calculate LCOM 96B for a given ES6 class source code.
* @param {string} classSource - The entire class code as a string.
* @returns {number} The LCOM 96B value, or -1 if parsing fails.
*/
function calculateLCOM96B(classSource) {
// Extract class body
const classBodyMatch = classSource.match(/class\s+\w+\s*{([\s\S]*)}/);
if (!classBodyMatch) return -1;
const classBody = classBodyMatch[1];
// Extract fields and methods (simplified, not for edge cases)
const fieldMatches = [...classBody.matchAll(/this\.(\w+)\s*=/g)];
const fields = [...new Set(fieldMatches.map(m => m[1]))];
const methodMatches = [...classBody.matchAll(/(\w+)\s*\(([^)]*)\)\s*{/g)];
const methods = methodMatches.map(m => m[1]).filter(m => m !== 'constructor');
// Map method to fields it accesses
const methodFieldAccess = methods.map(method => {
// Find method body
const methodRegex = new RegExp(method + "\\s*\\([^\\)]*\\)\\s*{([\\s\\S]*?)}", "g");
const methodBodyMatch = methodRegex.exec(classBody);
const methodBody = methodBodyMatch ? methodBodyMatch[1] : "";
// Find which fields are accessed in the method
const accessedFields = fields.filter(field =>
new RegExp(`this\\.${field}\\b`).test(methodBody)
);
return accessedFields;
});
// Sum for all fields: number of methods that access that field
let sum_mA = 0;
for (const field of fields) {
let count = 0;
for (const accessed of methodFieldAccess) {
if (accessed.includes(field)) count++;
}
sum_mA += count;
}
const m = methods.length;
const a = fields.length;
if (m === 0 || a === 0) return -1; // Avoid division by zero
// LCOM 96B formula
const lcom = 1 - (sum_mA / (m * a));
return Number(lcom.toFixed(3));
}
// Example usage:
const code = `
class ShoppingCart {
constructor() {
this.items = [];
this.total = 0;
}
addItem(item, price) {
this.items.push(item);
this.total += price;
}
getTotal() {
return this.total;
}
clearCart() {
this.items = [];
this.total = 0;
}
}
`;
console.log('LCOM 96B:', calculateLCOM96B(code)); // Output: LCOM 96B: 0.167
Conclusion: LCOM 96B as a Compass for Software Quality
Lack of Cohesion in Methods 96B provides a quantitative lens through which you can evaluate and improve your code’s architecture. By routinely measuring and addressing cohesion, you foster a codebase that’s easier to reason about, less error-prone, and more adaptable to change. Remember, metrics are most powerful when paired with good engineering judgment, team collaboration, and a commitment to continuous improvement.
Whether you’re a solo developer or part of a large team, investing in class cohesion pays off in the long run. Use LCOM 96B not as a hammer, but as a compass—guiding you toward cleaner, more maintainable code.