기능
Function
1. 함수 매개변수(2개 미만이 바람직함)함수 매개변수의 수가 많기 때문에 함수를 테스트하는 것이 더 쉽습니다. 3개 이상의 선택적 매개변수를 사용하면 조합이 폭발적으로 늘어나며 테스트할 독립 매개변수 시나리오가 엄청나게 많아지게 됩니다.
매개변수가 없는 것이 이상적인 상황입니다. 1~2개는 괜찮고 3개는 피하는 것이 좋습니다. 더 이상 강화가 필요합니다. 일반적으로 함수에 2개 이상의 매개변수가 있는 경우 처리할 항목이 너무 많다는 의미입니다. 많은 양의 데이터를 전달해야 하는 경우 상위 수준 개체를 매개변수로 캡슐화하는 것이 좋습니다.
나쁨: function createMenu(string $title, string $body, string $buttonText, bool $cancellable): void
{
// ...
}
좋음:
class MenuConfig { public $title; public $body; public $buttonText; public $cancellable = false; } $config = new MenuConfig(); $config->title = 'Foo'; $config->body = 'Bar'; $config->buttonText = 'Baz'; $config->cancellable = true; function createMenu(MenuConfig $config): void { // ... }2. 함수는 한 가지 일만 해야 합니다.
이것은 소프트웨어 엔지니어링에서 가장 중요한 규칙입니다. 함수가 두 가지 이상의 작업을 수행하면 구현, 테스트 및 이해가 어렵습니다. 함수를 단 하나의 함수로 분할하면 리팩토링하기가 더 쉬워지고 코드를 읽기가 더 명확해집니다. 이 규칙만 따르면 대부분의 개발자보다 앞서 나갈 수 있습니다. ㅋㅋㅋ 다시 에
4. 함수에는 하나의 추상화 계층만 있어야 합니다
추상화 수준이 너무 많으면 함수가 너무 많은 일을 처리하게 됩니다. 테스트를 단순화하기 위해 재사용성과 사용 편의성을 향상하려면 분할 기능이 필요합니다. (번역자 주: 여기 샘플 코드로 판단하면 너무 많은 중첩을 참조해야 합니다.) Bad:
function emailClients(array $clients): void { foreach ($clients as $client) { $clientRecord = $db->find($client); if ($clientRecord->isActive()) { email($client); } } }
Bad:
루프에서 일부 메서드를 추출했지만parseBetterJSAlternative() < /code>이 방법은 여전히 매우 복잡하고 테스트에 도움이 되지 않습니다. function emailClients(array $clients): void
{
$activeClients = activeClients($clients);
array_walk($activeClients, 'email');
}
function activeClients(array $clients): array
{
return array_filter($clients, 'isClientActive');
}
function isClientActive(int $client): bool
{
$clientRecord = $db->find($client);
return $clientRecord->isActive();
}
좋음:가장 좋은 해결책은 parseBetterJSAlternative()
메서드의 종속성을 제거하는 것입니다. class Email
{
//...
public function handle(): void
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
// 啥?handle处理一个消息干嘛了?是往一个文件里写吗?
$message->handle();
이 방법으로 종속성을 모의
하고 BetterJSAlternative::parse()
가 예상대로 실행되는지 테스트할 수 있습니다. mock
,并测试BetterJSAlternative::parse()
运行是否符合预期。
5. 不要用flag作为函数的参数
flag就是在告诉大家,这个方法里处理很多事。前面刚说过,一个函数应当只做一件事。 把不同flag的代码拆分到多个函数里。
坏:
class Email
{
//...
public function send(): void
{
mail($this->to, $this->subject, $this->body);
}
}
$message = new Email(...);
// 简单明了
$message->send();
避免副作用
一个函数做了比获取一个值然后返回另外一个值或值们会产生副作用如果。副作用可能是写入一个文件,修改某些全局变量或者偶然的把你全部的钱给了陌生人。
现在,你的确需要在一个程序或者场合里要有副作用,像之前的例子,你也许需要写一个文件。你想要做的是把你做这些的地方集中起来。不要用几个函数和类来写入一个特定的文件。用一个服务来做它,一个只有一个。
重点是避免常见陷阱比如对象间共享无结构的数据,使用可以写入任何的可变数据类型,不集中处理副作用发生的地方。如果你做了这些你就会比大多数程序员快乐。
坏:
function parseBetterJSAlternative(string $code): void
{
$regexes = [
// ...
];
$statements = explode(' ', $code);
$tokens = [];
foreach ($regexes as $regex) {
foreach ($statements as $statement) {
// ...
}
}
$ast = [];
foreach ($tokens as $token) {
// lex...
}
foreach ($ast as $node) {
// parse...
}
}
好:
function tokenize(string $code): array
{
$regexes = [
// ...
];
$statements = explode(' ', $code);
$tokens = [];
foreach ($regexes as $regex) {
foreach ($statements as $statement) {
$tokens[] = /* ... */;
}
}
return $tokens;
}
function lexer(array $tokens): array
{
$ast = [];
foreach ($tokens as $token) {
$ast[] = /* ... */;
}
return $ast;
}
function parseBetterJSAlternative(string $code): void
{
$tokens = tokenize($code);
$ast = lexer($tokens);
foreach ($ast as $node) {
// 解析逻辑...
}
}
6. 不要写全局函数
在大多数语言中污染全局变量是一个坏的实践,因为你可能和其他类库冲突 并且调用你api的人直到他们捕获异常才知道踩坑了。让我们思考一种场景: 如果你想配置一个数组,你可能会写一个全局函数config()
,但是他可能 和试着做同样事的其他类库冲突。
坏:
class Tokenizer
{
public function tokenize(string $code): array
{
$regexes = [
// ...
];
$statements = explode(' ', $code);
$tokens = [];
foreach ($regexes as $regex) {
foreach ($statements as $statement) {
$tokens[] = /* ... */;
}
}
return $tokens;
}
}
class Lexer
{
public function lexify(array $tokens): array
{
$ast = [];
foreach ($tokens as $token) {
$ast[] = /* ... */;
}
return $ast;
}
}
class BetterJSAlternative
{
private $tokenizer;
private $lexer;
public function __construct(Tokenizer $tokenizer, Lexer $lexer)
{
$this->tokenizer = $tokenizer;
$this->lexer = $lexer;
}
public function parse(string $code): void
{
$tokens = $this->tokenizer->tokenize($code);
$ast = $this->lexer->lexify($tokens);
foreach ($ast as $node) {
// 解析逻辑...
}
}
}
好:
function createFile(string $name, bool $temp = false): void
{
if ($temp) {
touch('./temp/'.$name);
} else {
touch($name);
}
}
好:
function createFile(string $name): void
{
touch($name);
}
function createTempFile(string $name): void
{
touch('./temp/'.$name);
}
现在你必须在程序中用 Configuration
5. 플래그를 함수 매개변수로 사용하지 마세요flag는 이 메소드가 많은 일을 처리한다는 것을 모든 사람에게 알려줍니다. 앞서 언급했듯이 함수는 한 가지 일만 수행해야 합니다. 다양한 플래그에 대한 코드를 여러 기능으로 분할합니다.
나쁜: // Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
$name = 'Ryan McDermott';
function splitIntoFirstAndLastName(): void
{
global $name;
$name = explode(' ', $name);
}
splitIntoFirstAndLastName();
var_dump($name); // ['Ryan', 'McDermott'];
부작용 방지
값을 얻는 것 이상의 기능을 수행하고 다른 값이나 값을 반환하는 함수는 다음과 같은 경우 부작용이 있습니다. 부작용은 파일에 쓰기, 일부 전역 변수 수정, 실수로 낯선 사람에게 모든 돈을 주는 것 등이 될 수 있습니다.
이제 프로그램이나 상황에 따라 부작용이 발생해야 할 수도 있습니다. 이전 예와 마찬가지로 파일을 작성해야 할 수도 있습니다. 당신이 원하는 것은 이 작업을 수행하는 장소를 중앙 집중화하는 것입니다. 특정 파일에 쓰기 위해 여러 함수와 클래스를 사용하지 마세요. 단 하나의 서비스로 수행하십시오.
객체 간에 구조화되지 않은 데이터를 공유하고, 무엇이든 쓸 수 있는 변경 가능한 데이터 유형을 사용하고, 부작용이 발생하는 위치에 초점을 맞추지 않는 등 일반적인 함정을 피하는 데 중점을 둡니다. 이렇게 하면 대부분의 프로그래머보다 더 행복해질 것입니다.
🎜🎜나쁨:🎜🎜 function splitIntoFirstAndLastName(string $name): array
{
return explode(' ', $name);
}
$name = 'Ryan McDermott';
$newName = splitIntoFirstAndLastName($name);
var_dump($name); // 'Ryan McDermott';
var_dump($newName); // ['Ryan', 'McDermott'];
🎜🎜좋음:🎜🎜 function config(): array
{
return [
'foo' => 'bar',
]
}
🎜🎜🎜6 전역 함수를 작성하지 마세요🎜🎜🎜 전역 변수를 오염시키는 것은 대부분의 언어에서 나쁜 습관입니다. 다른 라이브러리와 충돌할 수 있으며 API를 호출하는 사람들은 예외를 포착할 때까지 문제가 있다는 것을 알지 못합니다. 시나리오를 생각해 봅시다. 배열을 구성하려는 경우 전역 함수 config()
를 작성할 수 있지만 동일한 작업을 수행하려는 다른 라이브러리와 충돌할 수 있습니다. 🎜🎜나쁨:🎜class Configuration
{
private $configuration = [];
public function __construct(array $configuration)
{
$this->configuration = $configuration;
}
public function get(string $key): ?string
{
return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
}
}
加载配置并创建 Configuration 类的实例
$configuration = new Configuration([
'foo' => 'bar',
]);
🎜좋음:🎜 class DBConnection
{
private static $instance;
private function __construct(string $dsn)
{
// ...
}
public static function getInstance(): DBConnection
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// ...
}
$singleton = DBConnection::getInstance();
🎜이제 프로그램에서 Configuration
인스턴스를 사용해야 합니다🎜🎜🎜🎜🎜7 싱글톤 패턴을 사용하지 마세요🎜🎜🎜싱글톤은 안티 패턴에 대한 설명은 다음과 같습니다. Brian Button의 설명: 🎜🎜🎜🎜은 항상 전역 인스턴스로 사용됩니다. 일반적으로 전역 인스턴스로 사용되는데, 왜 그렇게 나쁜가요? 인터페이스를 통해 노출하는 대신 애플리케이션의 종속성을 코드에 숨기기 때문에 전달을 피하기 위해 전역 인스턴스를 만드는 것은 코드 냄새입니다.🎜🎜 🎜🎜그들은 자신의 생성과 수명 주기를 제어한다는 사실 때문에 단일 책임 원칙을 위반합니다.🎜🎜🎜🎜이들은 본질적으로 코드가 긴밀하게 결합되도록 하기 때문에 많은 경우에 테스트 중에 코드를 속이는 것이 다소 어렵습니다. 🎜🎜🎜프로그램이 진행되는 동안 항상 상태를 전달합니다. 이는 애플리케이션의 수명 동안 상태를 전달합니다. 각 단위 테스트가 서로 독립적이어야 하기 때문에 테스트를 주문해야 하는 상황이 발생할 수 있기 때문에 테스트에 대한 또 다른 문제입니다. .🎜Misko Hevery가 쓴 싱글톤 패턴의 근본 원인을 설명하는 아주 좋은 기사입니다((http://misko.hevery.com/2008/08/25/root-cause-of-singletons/).
나쁨:
class DBConnection
{
public function __construct(string $dsn)
{
// ...
}
// ...
}
좋음:
if ($article->state === 'published') {
// ...
}
DBConnection
클래스의 인스턴스를 생성하고 DSN을 통해 구성합니다.DBConnection
类的实例并通过 DSN 配置.
$connection = new DBConnection($dsn);
现在你必须在程序中 使用 DBConnection
$connection = new DBConnection($ dsn) ;
이제 프로그램에서 DBConnection
인스턴스를 사용해야 합니다
8 조건문 캡슐화하기
나쁨:
if ($article->isPublished()) {
// ...
}
좋음: function isDOMNodeNotPresent(\DOMNode $node): bool
{
// ...
}
if (!isDOMNodeNotPresent($node))
{
// ...
}
9. 조건부 판단을 피하세요
Bad:
function isDOMNodePresent(\DOMNode $node): bool
{
// ...
}
if (isDOMNodePresent($node)) {
// ...
}
Good: class Airplane
{
// ...
public function getCruisingAltitude(): int
{
switch ($this->type) {
case '777':
return $this->getMaxAltitude() - $this->getPassengerCount();
case 'Air Force One':
return $this->getMaxAltitude();
case 'Cessna':
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
}
10 불가능해 보이는 작업입니다. 사람들이 이 문장을 처음 들었을 때 , 그들은 "if 문 없이 무엇을 할 수 있습니까?"라고 말합니다. 대답은 다형성을 사용하여 여러 시나리오에서 동일한 작업을 수행할 수 있다는 것입니다. "이렇게 해도 괜찮지만 왜 해야 합니까?" 함수는 한 가지 작업만 수행해야 한다는 클린 코드 원칙입니다. if 문이 포함된 클래스와 함수가 많으면 함수는 한 가지 작업만 수행한다는 점을 기억하세요. 함수는 모든 유형의 매개변수를 허용할 수 있습니다. 첫 번째는 API를 통합하는 것입니다. 12. 유형 검사를 피하세요(2부)
문자열, 정수, 배열과 같은 기본 기본 값을 사용하는 경우 필요한 버전은 다형성이 필요하지 않고 유형 감지가 필요한 PHP 7+이므로 다음을 수행해야 합니다. 유형 선언 또는 엄격 모드를 고려하십시오. 표준 PHP 구문을 기반으로 정적 유형을 제공할 때의 문제점은 이를 제대로 수행하려면 말도 안 되는 일이 많이 필요하다는 것입니다. 이는 단지 가독성의 손실에 관계없이 PHP를 깨끗하게 유지하기 위한 것 같습니다. , 좋은 테스트를 작성하고 코드 검토를 수행할 수 없으면 안전을 보장하기 위해 PHP 엄격한 유형 선언과 엄격 모드를 사용하세요.
interface Airplane
{
// ...
public function getCruisingAltitude(): int;
}
class Boeing777 implements Airplane
{
// ...
public function getCruisingAltitude(): int
{
return $this->getMaxAltitude() - $this->getPassengerCount();
}
}
class AirForceOne implements Airplane
{
// ...
public function getCruisingAltitude(): int
{
return $this->getMaxAltitude();
}
}
class Cessna implements Airplane
{
// ...
public function getCruisingAltitude(): int
{
return $this->getMaxAltitude() - $this->getFuelExpenditure();
}
}
13. 좀비 코드는 중복 코드만큼 나쁩니다. 호출되지 않은 경우 삭제하면 안전합니다. 나쁜: function travelToTexas($vehicle): void
{
if ($vehicle instanceof Bicycle) {
$vehicle->pedalTo(new Location('texas'));
} elseif ($vehicle instanceof Car) {
$vehicle->driveTo(new Location('texas'));
}
}
좋은:
function travelToTexas(Vehicle $vehicle): void
{
$vehicle->travelTo(new Location('texas'));
}