என் Best Practices – பொது

1. Comment-கள் – சாபமும் வரமும்

நல்ல comment எழுதுவது ஒரு கலை. யோசித்து எழுதினால் வரம்; இல்லையேல் பெரும்பாலும் வீண்.

Self-documenting code என்றால் “comment-களே இல்லை” என்று அர்த்தமல்ல. Code தானே எப்படி என்பதை முடிந்தவரை வெளிப்படுத்த வேண்டும்; comment-கள் ஏன் என்பதையும், வெளிப்படையாகத் தெரியாத நிபந்தனைகளையும், ஒரு API-இன் ஒப்பந்தத்தையும் (contract) சேர்க்கின்றன.

கீழே self-documenting code-க்கான சில எடுத்துக்காட்டுகள், நல்ல மற்றும் மோசமான comment-களுடன்.

Comment-கள் எளிதில் காலாவதியாகிவிடும் (outdated) என்பது ஒரு பொதுவான வாதம். உண்மையில், இந்தப் பிரச்சினை variable, method பெயர்களுக்கும் சமமாகவே பொருந்தும்!

ஐயோ – இதை ஒருபோதும் செய்யாதீர்கள்:

int age = 32; // set the value of the age integer to 32

Self-documenting: variable-களுக்கு அர்த்தமுள்ள பெயர் வைத்தாலே பாதி வேலை முடிந்தது

மோசம்:

var dateTime = DateTimeUtil.getNow();

நல்லது:

var now = DateTimeUtil.getNow();

இன்னொரு எடுத்துக்காட்டு:

மோசம்:

User user = this.getBruce();

மோசம்:

// testing user without permission
User user = this.getBruce();

நல்லது:

User userWithoutPermission = this.getBruce();

Self-documenting: method-களுக்கு அர்த்தமுள்ள பெயர்கள்

மோசம்:

public String sanitizeInput(String input) {
    // replace underscore with spaces
    return input.replaceAll("_", " ");
}

நல்லது:

public String replaceUnderscoreWithSpaces(String input) {
    return input.replaceAll("_", " ");
}

Self-documenting: Private method-கள்

மோசம்: இந்த method, validation, கணக்கீடு, வணிக விதி ஆகிய அனைத்தையும் ஒரே நேரத்தில் புரிந்துகொள்ள வாசகரைக் கட்டாயப்படுத்துகிறது.

public Invoice createInvoice(Order order, Customer customer) {
    if (order.items().isEmpty()) {
        throw new IllegalArgumentException("Order needs at least one item");
    }

    BigDecimal subtotal = order.items().stream()
            .map(item -> item.unitPrice().multiply(BigDecimal.valueOf(item.quantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
    BigDecimal discount = customer.isPremium()
            ? subtotal.multiply(new BigDecimal("0.10"))
            : BigDecimal.ZERO;

    return new Invoice(order.id(), subtotal, discount, subtotal.subtract(discount));
}

நல்லது: Public method முழு ஓட்டத்தையும் ஒரு கதைபோலச் சொல்கிறது. Private, stateless method-கள் ஒவ்வொரு தனி நோக்கத்துக்கும் பெயரிடுகின்றன; அவற்றின் சார்புகளை parameter-கள், return மதிப்புகள் மூலம் வெளிப்படையாக்குகின்றன.

public Invoice createInvoice(Order order, Customer customer) {
    requireItems(order);
    BigDecimal subtotal = calculateSubtotal(order.items());
    BigDecimal discount = loyaltyDiscountFor(customer, subtotal);

    return invoiceFor(order, subtotal, discount);
}

private static void requireItems(Order order) {
    if (order.items().isEmpty()) {
        throw new IllegalArgumentException("Order needs at least one item");
    }
}

private static BigDecimal calculateSubtotal(List<OrderItem> items) {
    return items.stream()
            .map(item -> item.unitPrice().multiply(BigDecimal.valueOf(item.quantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add);
}

private static BigDecimal loyaltyDiscountFor(Customer customer, BigDecimal subtotal) {
    return customer.isPremium()
            ? subtotal.multiply(new BigDecimal("0.10"))
            : BigDecimal.ZERO;
}

private static Invoice invoiceFor(
        Order order,
        BigDecimal subtotal,
        BigDecimal discount
) {
    return new Invoice(order.id(), subtotal, discount, subtotal.subtract(discount));
}

ஒவ்வொரு வரிக்கும் தனி method தேவையில்லை. துல்லியமாகப் பெயரிடக்கூடிய, ஒருங்கிணைந்த ஒரு சிந்தனையைத்தான் பிரித்தெடுக்க வேண்டும். மாறாக, processData() அல்லது handleStuff() போன்ற பெயர்களால் எந்தப் பயனும் இல்லை.

பொது விதி: என்ன நடக்கிறது என்பதை அல்ல, ஏன் நடக்கிறது என்பதை comment-இல் எழுதுங்கள்

இங்கே என்ன நடக்கிறது என்பது தெளிவு:

public Duration retryDelay(int retryCount) {
    long seconds = Math.min(1L << retryCount, 60);
    return Duration.ofSeconds(seconds);
}

ஆனால் ஏன் நடக்கிறது?

இன்னும் சிறப்பாக:

public Duration retryDelay(int retryCount) {
    // Cap the delay so an outage never blocks queued jobs for more than one minute.
    long seconds = Math.min(1L << retryCount, 60);
    return Duration.ofSeconds(seconds);
}

இன்னொரு நிஜ உலக எடுத்துக்காட்டு. Ajax loading spinner-ஐக் காட்டவும் மறைக்கவும் உள்ள method-கள் இங்கே உள்ளன. ஆனால் மொத்தம் நான்கு method-கள். ஏன் என்பது முதல் பார்வையில் முழுமையாகத் தெரியவில்லை:

function showAjaxLoader() {
    $('.layout-ajax-loader').children().first().show();
}

function hideAjaxLoader() {
    $('.layout-ajax-loader').children().first().hide();
}

function suppressAjaxLoader() {
    getOnlyElementByClassOrNull('layout-ajax-loader').style.display = 'none';
}

function releaseAjaxLoader() {
    getOnlyElementByClassOrNull('layout-ajax-loader').style.display = '';
}

இந்த method-கள் அனைத்தும் கிட்டத்தட்ட ஒன்றுபோலவே தெரிகின்றன; எல்லாமே எப்படியோ CSS மூலம் visibility-ஐ மாற்றுவதுபோல் உள்ளன. ஒரு சிறிய comment சேர்த்தாலே, ஒவ்வொன்றும் எப்போது பயன்படும் என்பது உடனே தெளிவாகிறது:

showAjaxLoader: function() {
    $('.layout-ajax-loader').children().first().show();
},

hideAjaxLoader: function() {
    $('.layout-ajax-loader').children().first().hide();
},

// take the control from primefaces and don't allow the spinner to show, no matter what
suppressAjaxLoader: function() {
    getOnlyElementByClassOrNull('layout-ajax-loader').style.display = 'none';
},

// reset the state after hiding, to give the control back to primefaces
releaseAjaxLoader: function() {
    getOnlyElementByClassOrNull('layout-ajax-loader').style.display = '';
},

இன்னொரு எடுத்துக்காட்டு:

மோசம்:

/**
 * Sets the tool tip text.
 *
 * @param text  the text of the tool tip
 */
public void setToolTipText(String text) {}

நல்லது:

/**
 * Registers the text to display in a tool tip. The text
 * displays when the cursor lingers over the component.
 *
 * @param text  the string to display. If the text is null,
 *              the tool tip is turned off for this component.
 */
public void setToolTipText(String text) {}

2021-06-21 அன்று நடந்த workshop-இல் விவாதம்: Javadoc-ஐப் பொதுவாகத் தவிர்க்க வேண்டும் என்பதில் எல்லா developer-களும் ஒத்த கருத்தில் இருந்தனர் (parameter-களின் தலைவலி, எப்போதும் காலாவதியாகிவிடும், பராமரிப்பதும் சிரமம்); ஒரு parameter-க்கு உண்மையிலேயே கூடுதல் விளக்கம் தேவைப்படும்போது, அல்லது இல்லையென்றால் நீண்ட method முழுவதையும் படிக்க வேண்டியிருக்கும்போது மட்டுமே அதைப் பயன்படுத்த வேண்டும்.

பொது விதிக்கு விதிவிலக்கு: என்ன நடக்கிறது என்பதை comment-இல் எழுதுங்கள் – இங்கே அதுதான் நல்லது

Code தானாகத் தெளிவாக இல்லாததால், comment வாசிப்பைக் கணிசமாக எளிதாக்கும்போது:

let isAlwaysAllowedKey = (
    e.which === 8  || /* BACKSPACE */
    e.which === 35 || /* END */
    e.which === 36 || /* HOME */
    e.which === 37 || /* LEFT */
    e.which === 38 || /* UP */
    e.which === 39 || /* RIGHT*/
    e.which === 40 || /* DOWN */
    e.which === 46 || /* DEL*/
    e.ctrlKey === true && e.which === 65 || /* CTRL + A */
    e.ctrlKey === true && e.which === 88 || /* CTRL + X */
    e.ctrlKey === true && e.which === 67 || /* CTRL + C */
    e.ctrlKey === true && e.which === 90   /* CTRL + Z */
)

Comment-கள் – Linux kernel code-இல் உள்ள கெட்ட வார்த்தைகள்

இறுதியாக Linux kernel-இலிருந்து ஒரு வேடிக்கையான சங்கதி – source code-இல் உள்ள அனைத்துக் கெட்ட வார்த்தைகளின் எண்ணிக்கை:

https://www.vidarholen.net/contents/wordcount/#fuck*,shit*,damn*,idiot*,retard*

Comment எழுதுவதை ரசியுங்கள்!

2. பெரிய program routine-களில் private method-கள் ஆபத்தானவை

கவனம்! இன்றைய object-oriented உலகில் எப்போதும் ஏராளமான private method-கள் இருக்கும். பொதுவாக இது பிரச்சினை இல்லை. இந்த method-கள் field-களை, அதாவது state-ஐ, அணுகும்போதும், பெரிய routine-களின் பகுதியாக இருக்கும்போதும்தான் அது பிரச்சினையாகிறது. இத்தகைய sub-method-கள் private-ஆகவும் stateless-ஆகவும் இருக்க வேண்டும். இல்லையென்றால், தனியாக அழைக்கப்படும்போது அவை பெரும்பாலும் சிக்கலை உண்டாக்கும். எப்படியும் யாராவது ஒரு developer அந்த sub-method-ஐ நேரடியாக அழைக்கும் யோசனைக்கு வருவார். தவறான (illegal) state-கள் bug-களுக்கான முக்கியக் காரணங்களில் ஒன்று. இது தொடர்பாக John Carmack (id Software, Doom) கூறிய சில கருத்துகள் பற்றி ஒரு சுவாரஸ்யமான கட்டுரை உள்ளது; code மிகவும் நிலையானதாக இருக்க வேண்டிய பயன்பாடுகளில் (எ.கா. விண்வெளிப் பயணம்) subroutine-கள் பெரும்பாலும் தடைசெய்யப்படுகின்றன, இதனால் code மேலும் deterministic ஆகிறது என்கிறது அது: http://number-none.com/blow/john_carmack_on_inlined_code.html

இந்தக் கதை நவீன stateless Java/Spring உலகுக்கு ஓரளவுக்கே பொருந்தும்; இருந்தாலும் சிந்திக்க இது ஒரு நல்ல தூண்டுதல்.