Om du någon gång har stirrat på ett villkor som if (!(isValid && isComplete)) och behövt sakta ner för att förstå vad det egentligen betyder, är du inte ensam. De Morgans lagar ger dig ett mekaniskt sätt att skriva om negerad logik till något som är mycket lättare att läsa.
De två lagarna
De Morgans lagar beskriver hur negation distribueras över AND och OR:
NOT (A AND B) == (NOT A) OR (NOT B)
NOT (A OR B) == (NOT A) AND (NOT B)
Uttryckt i kod:
!(a && b) === !a || !b
!(a || b) === !a && !b
Lägg märke till mönstret: när du trycker en NOT genom parenteserna vänds AND till OR, och OR vänds till AND. Negationen flyttas till varje enskild term.
En verklig refaktorering
Ta denna valideringskontroll:
if (!(isValid && isComplete)) {
showError();
}
Det lyder: "om det INTE är fallet att formuläret är både giltigt och komplett." Det var en mun full. Om vi tillämpar De Morgans lag:
if (!isValid || !isComplete) {
showError();
}
Detta läses betydligt mer naturligt: "om det är ogiltigt, eller om det är inkomplett, visa ett felmeddelande." Samma logik, men dramatiskt lättare att förstå vid en snabb genomläsning.
Ett annat exempel, i omvänd riktning
De Morgans lagar fungerar åt båda hållen. Ibland är den negerade formen faktiskt tydligare:
// Original
if (!isOnline && !hasCachedData) {
showOfflineMessage();
}
// Ekvivalent, via De Morgans lag
if (!(isOnline || hasCachedData)) {
showOfflineMessage();
}
Här kommunicerar den andra versionen förmodligen avsikten bättre: "om varken online eller cachad data är tillgänglig." Använd vilken form som gör avsikten tydligast för läsaren — De Morgans lagar garanterar bara att båda är likvärdiga, så du är fri att välja.
Varför detta spelar roll för kodgranskning
Negerade sammansatta villkor är en vanlig källa till missförstånd i pull requests. När du ser en dubbel negation eller en negerad grupp, försök att mentalt tillämpa De Morgans lag för att kontrollera om den förenklade versionen matchar författarens avsikt — och om den matchar det faktiska beteende som testerna förväntar sig.
!(A op B) blir (!A) op' (!B), där op' är motsatsen till op.