良いコードの主な原則

多様なプログラミングの20年にわたって、私は良いコードの主な原則を定式化し、確信しています。 それに基づいて、同僚と私は最悪のコードを整理し、互換性のないプログラマーのチームに団結し、何年もの間あまり泣き言を言わずにシステムを維持しました。

この記事を読む: 15分
テクニックの理解: 10分
有形の結果: 30分

だから

なんで?


悪いコードはどうですか?


同じ瞬間を二度指摘したのも不思議ではありません。 実際、ほとんどすべては、プログラマーがコードを理解できないという事実に帰着します。

私は叫んで目を覚ます

悪いコードはゆっくりと修正され、変更は驚きをもたらします。 これは理解できることです。プログラマーはコードが何をしているのかわからないので、何か新しいものを作成する代わりにそれを把握する必要があります。 実際、作者だけが実際にプログラムを修正できることがよくあります。

したがって、優れたコードの最も重要な兆候の1つは、そのわかりやすさです。 明快さは純粋に人間の概念です。 コンパイラは何と呼ばれるかを気にしません;本質を掘り下げません; コードを読むのは人だけです。 メソッドの名前からメソッドが何をするのかを想像できるのは人だけです。 変数の名前を読んだ人だけが、変数に保存されたデータの本質をすぐに見ることができます。

コードのわかりやすさは名前により依存していると確信しています。 クラス、メソッド、変数の名前から。 名前が明確に定式化されていればいるほど、コードが読みやすくなります。

たとえば、モバイルデバイスに通知を送信するためのコードは次のとおりです。

function locator($device) {
    // Can we locate the device?
    if ($device->token != "" && $device->expire <= $now) {
        return false;
    }

    $modelNotifier = new ModelNotifier($device); 
    return $modelNotifier->go();
}

, - . .


. , , $device->expire , . ModelNotifier->go() , . , , token . , .

, : token!="", . , , .

, . — . , ; , . , .

.

?


, — .

, , , . , — — .

:

function locator($device) {
    // Can we locate the device?
    if ($device->token != "" && $device->expire <= $now) {
        return false;
    }

    $modelNotifier = new ModelNotifier($device); 
    return $modelNotifier->go();
}

, , : “ ?”

:

function ModelDevice::isLocatable() {
    return ($this->token != "" && $this->expire <= $now);
}

…

function locateAndNotifyDevice(ModelDevice $device) {
    if (!$device->isLocatable()) {
        return false;
    }

    $modelNotifier = new ModelNotifier($device);
    return $modelNotifier->go();
}

, , - , .

. isLocatable(). : , :

  1. ?
  2. ?

: , .

:

function isDeviceExpired() {
    return ($this->expireAtUnixtime >= $now);
}

function isOurTokenValid() {
    return ($this->token != "");
}

function isLocatable() {
    return ($this->isOurTokenValid() && !$this->isDeviceExpired());
}

. : isLocatable() , . isDeviceExpired(), , .

.

, , , token . , : .

, : . Instant win!

?


, . ! , .

-, . , , . , : . , , . . , ? :)

-, - (“ ?”) (“ ?”) . isLocatable(), : , . — , . “” locateDevice(), ; , , . — , .

-, , CPU, . , — 1/3 *. , , . , . , , . , .

*300ms — .


:

function getConfig() {
    if ($this->hasOption('configs')) {
        $configs = $this->getOption('configs');
    } else if ($this->hasOption('config')) {
        $configs = [ $this->getOption('config') ];
    }

    if (isset($configs)) {
        $root = $this->getOption('root', null);
        if (!$root) {
            if (isset($_SERVER['PWD'])) {
                $root = $_SERVER['PWD'];
            } else if (isset($_SERVER['DOCUMENT_ROOT'])) {
                $root = $_SERVER['DOCUMENT_ROOT'];
            }
        }

        $configs->root = $root;
    }

    return $configs;
}

: ( ), . :

function grabConfiguration() {
    if ($this->hasOption('configs')) {
        return $this->getOption('configs');
    } else if ($this->hasOption('config')) {
        return [ $this->getOption('config') ];
    }
}

function figureOutRootFolder() {
    $root = $this->getOption('root', null);
    if (!$root) {
        $root = $_SERVER['PWD'];
    }
    if (!$root) {
        $root = $_SERVER['DOCUMENT_ROOT'];
    }
    return $root;
}

function getConfigs() {
    $configs = $this->grabConfiguration();  
    if (!is_null($configs)) {
        $configs->rootFolder = $this->figureOutRootFolder();        
    }
    return $configs;
}

iOS:

if ([imageEntry objectForKey:@"fullscreenUrl"]==nil) {
    NSString *kind = [imageEntry objectForKey:@"kind"];
    if ([kind isEqualToString:@"pdf"]) {
        UIImage *i = [UIImage imageNamed:@"galleryPdfIcon"];
        [self.uiImagesCache setObject:i forKey:@(photoIndex)];
        *photoSize = NIPhotoScrollViewPhotoSizeOriginal;
        return i;
    } else if ([kind isEqualToString:@"video"]) {
        UIImage *i = [UIImage imageNamed:@"galleryVideoIcon"];
        [self.uiImagesCache setObject:i forKey:@(photoIndex)];
        *photoSize = NIPhotoScrollViewPhotoSizeOriginal;
        return i;
    } else {
        UIImage *i = [UIImage imageNamed:@"galleryBrokenImage"];
        [self.uiImagesCache setObject:i forKey:@(photoIndex)];
        *photoSize = NIPhotoScrollViewPhotoSizeOriginal;
        return i;
    }
}

, . . :

- (UIImage *) genericImageForKind:(NSString *) imageKind {
    UIImage *candidate = nil;

    if ([kind isEqualToString:@"pdf"]) {
        candidate = [UIImage imageNamed:@"galleryPdfIcon"];
    } else if ([kind isEqualToString:@"video"]) {
        candidate = [UIImage imageNamed:@"galleryVideoIcon"];
    } else {
        candidate = [UIImage imageNamed:@"galleryBrokenImage"];
    }

    return candidate;
}

…

if ([imageEntry objectForKey:@"fullscreenUrl"]==nil) {
    NSString *kind = [imageEntry objectForKey:@"kind"];
    UIImage *genericImage = [self genericImageForKind:kind];
    if (genericImage) {
        [self.uiImagesCache setObject:genericImage forKey:@(photoIndex)];
        *photoSize = NIPhotoScrollViewPhotoSizeOriginal;
    } 

    return nil;
}

SQL . , , , , .

function(err, dResults) {
    if (err) {
        console.log(err);
        callback(err);
        return;
    }

    var i=0, lastDownloaded = null;
    var unixtimes = Object.keys(dResults);
    for(i=0;i<unixtimes.length;i++) {
        if (dResults[unixtimes[i]].isDownloaded) { 
            lastDownloaded = unixtimes[i];
        }
    }

    for(i=0;i<unixtimes.length;i++) {
        if (unixtimes[i]>lastDownloaded) {
            delete dResults[unixtimes[i]];
        }
    }

    self.getOpenPrice(symbol, function(err, openPrice) {
        callback(err, dResults, openPrice);
    });
}

:

function getLastDownloadedUnixtime(entries) {
    var lastDownloadedUnixtime = null;

    var unixtimes = Object.keys(entries);
    unixtimes.forEach(function(unixtime) {
        if (entries[unixtime].isDownloaded) { 
            lastDownloadedUnixtime = unixtime;
        }
    });

    return lastDownloadedUnixtime;
}

function cleanupEntriesNewerThanLastDownloadedUnixtime(entries, lastDownloadedUnixtime) {
    var unixtimes = Object.keys(entries);
    for(var i=0;i<unixtimes.length;i++) {
        if (unixtimes[i]>lastDownloadedUnixtime) {
            delete entries[unixtimes[i]];
        }
    }
}

…

function(err, dResults) {
    if (err) {
        console.log(err);
        callback(err);
        return;
    }

    var lastDownloadedUnixtime = getLastDownloadedUnixtime(dResults);
    cleanupEntriesNewerThanLastDownloadedUnixtime(dResults, lastDownloadedUnixtime);

    self.getOpenPrice(symbol, function(err, openPrice) {
        callback(err, dResults, openPrice);
    });
}

XML Amazon S3. , , XML :

strcat(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
strcat(xml, "<Delete>\n");
strcat(xml, "<Quiet>true</Quiet>\n");

for (uint32_t i=0;i<batchCount;i++) {
    char *escapedPath=Deleter::xmlEscape(batch[i]);
    strcat(xml, "\t<Object><Key>");
    strcat(xml, escapedPath);
    free(escapedPath);
    strcat(xml, "</Key></Object>\n");
    LOG(LOG_DBG, "[Delete] %s", batch[i]);
}

strcat(xml, "</Delete>\n");

, :

void addPathToDeleteXml(char *xml, char *path) {
    char *escapedPath=Deleter::xmlEscape(path);
    strcat(xml, "\t<Object><Key>");
    strcat(xml, escapedPath);
    strcat(xml, "</Key></Object>\n");
    free(escapedPath);
}

void addBatchOfPathsToXml(char *xml, char **batch, uint32_t batchCount) {
    for (uint32_t i=0;i<batchCount;i++) {
        addPathToDeleteXml(xml, batch[i]);
        LOG(LOG_DBG, "[Delete] %s", batch[i]);
    }
}

…

strcat(xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
strcat(xml, "<Delete>\n");
strcat(xml, "<Quiet>true</Quiet>\n");

addBatchOfPathsToXml(xml, batch, batchCount);

strcat(xml, "</Delete>\n");



, — . : , - . , - , - , , .

, . , . , , , .

. , . , . , .


— . , : , , .

. — , - .

. , , , autocomplete.

Update: «if (true) return true» , .. , .

Source: https://habr.com/ru/post/J150868/


All Articles