クリアコードコアクリア(SOLID,3章関数)


に入る


SOLIDとは?
クリーンコードで有名なロバート・マーティンは、オブジェクト向けに良好な設計の5つの原則を提案した.

単一責任原則SRP


SRPとは?


一つの階級は一つの責任しか負わない.
  • クラスには1つの機能しかありません.変更によってクラスを変更できます.
    やらなければならない理由は一つしかない.
  • SRP責任が明確で、変更の影響を受けない
    遅くなるかもしれません.
  • は毒性とメンテナンスを容易に向上させる.
  • 実戦では、容易ではありませんが、常に覚えておきましょう!


  • 説明:


    左のロボットを見て、4つの機能を実行します.ロボットはクラスとして、1つのクラスに複数の責任があることを意味します.この問題を解決するために,右図に示すように,4つの責任ごとに1つのクラスに分類される.

    オープンクローズ原則OCP


    OCPとは?


    ソフトウェア要素は拡張で開き、変更で閉じます.
  • 変更のコストをできるだけ削減し、
    最大化する.
  • 要件を変更または追加した場合、既存のコンポーネント
    何の修正もせず、コンポーネントを簡単に拡張できます.
    使用します.
  • オブジェクト向けの抽象性と多形性を利用する.


  • 説明:


    左図に示すように、OCPが適用されない場合、既存のCut機能が機能しない例として、Paint機能が追加される.
    すなわち、クラスを変更する現在の操作は、クラスを使用するすべてのシステムに影響します.
    OCPの目標は、クラスの既存の操作を変更せずにクラスを拡張することです.クラスがより多くの機能を実行する場合は、既存の機能を変更せずにこれらの機能を追加するのが理想的です.

    リスコフ互換原則LSP


    リスコフ置換の原則とは何ですか。


    サブタイプは常にベースタイプに置き換える必要があります.
  • サブタイプには、ベースタイプ承諾の約定(アクセス制限者および例外を含む)が含まれます.
    守らなければならない.
  • クラス継承は、インタフェース継承によって拡張性を得る.
  • インタフェースを使用して、最大の成形性と拡張性を実現
    もっといいです.
  • による
  • の合成(合成)も可能である.



    説明:


    上図は、親ロボットがcoffeeクラスを渡す図です.カプチーノは、エスプレッソコーヒーがコーヒーの種類であるため、サブロボットの配送は許可されているが、配送は許可されていない例である.
    つまり、子クラスが親と同じ操作を実行できない場合、エラーが発生する可能性があります.
    子クラスは親クラスができることをすべてしなければならない.このプロセスを継承と呼ぶ.
    n/a.ターゲット
    この原則は、エラーが発生することなく、親または子が同じ方法で使用できるように一貫性を保つことです.

    インタフェース分離の原則ISP


    界面分離の原則とは何ですか。


    自分が使わないインタフェースを実現しないでください.
  • は、最小限のインタフェースのみを実現します.
  • クラスを使用するクライアントが複数ある場合、これらのクライアントはクラスの特定の部分のみを使用する場合、それらを複数のインタフェースに分類し、クライアントに必要な機能のみを渡す.
  • SRPがクラスの単一責任である場合、ISPはインタフェースの単一責任
  • である.



    説明:


    左の図は、ロボットの用途が異なるが不要なすべての機能を実現した例で、右の図はロボットの用途に応じて機能を分離することを示しています.
    クラスが不要な機能を実行する必要がある場合は、エラーが発生する可能性があります.したがって,インタフェースを分離し,クラスが必要な機能のみを継承できるようにするのがこのルールである.
    この原則の目標は、クラスが必要なワークセットのみを実行するように、ワークセットをより小さなセットに分割することです.

    依存性逆転の原則DIP


    依存性逆転の原則とは何ですか。


    親モデルは下位モデルに依存できません.どちらも抽象化に頼らなければならない.
    抽象化は細部に頼ることができない.細部は抽象化によって変わる.
  • サブモデルの変更は、親モジュールの関係の変更を要求することを中断します.
  • 実際の使用関係は変わらないが、抽象的に情報を伝達し、関係を緩やかにする
  • .



    説明:


    左の図はロボットの腕にピザを切るだけの機能を示しており、右のロボットはいつでも腕を変えて新しい道具を使うことができます.
    すなわち,クラス実行タスクは実装体クラスに依存せず抽象に依存し,複数の具体的なクラスを用いることができる.
    新しいクレジットカード会社を追加したら?
    @RequestMapping(value = "/api/payment", method = RequestMethod.POST)
    public void pay(@RequestBody CardPaymentDto.PaymentRequest req){
    	if(req.getType() == CardType.SHINHAN) {
    		shinhanCardPaymentService.pay(req);
    	} else if(req.getType() == CardType.WOORI){
    		wooriCardPaymentService.pay(req);
    	}
    }
    拡張が鈍い.

    ->どちらも抽象的なインタフェースに依存する.
    class PaymentController {
    	@RequestMapping(value = "/payment", method = RequestMethod.POST)
    	public void pay(@RequestBody CardPaymentDto.PaymentRequest req) {
    		final CardPaymentService cardPaymentService = cardPaymentFactory.getType(req.getType());
    		cardPaymentService.pay(req);
    	}
    }
    
    *public interface CardPaymentService {
    	void pay(CardPaymentDto.PaymentRequest req);
    }*
    
    public class ShinhanCardPaymentService implements CardPaymentService {
    	@Override
    	public void pay(CardPaymentDto.PaymentRequest req) {
    		shinhanCardApi.pay(req);
    	}
    }
    

    簡潔な関数の作成

    public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) throws Exception {
    	boolean isTestPage = pageData.hasAttribute("Test");
    	if (isTestPage) {
    		WikiPage testPage = pageData.getWikiPage();
    		StringBuffer newPageContent = new StringBuffer();
    		includeSetupPages(testPage, newPageContent, isSuite);
    		newPageContent.append(pageData.getContent());
    		includeTeardownPages(testPage, newPageContent, isSuite);
    		pageData.setContent(newPageContent.toString());
    	}
    	return pageData.getHtml();
    }
    「関数が長くて、いろいろな機能が混ざっています...」
    public static String renderPageWithSetupsAndTeardowns( PageData pageData, boolean isSuite) throws Exception {
    	if (isTestPage(pageData))
    		includeSetupAndTeardownPages(pageData, isSuite);
    	return pageData.getHtml();
    }
    引き裂く.
    関数内の抽象化レベルを一致させます.

    1つのみ実行(SRP)、変更を閉じる(OCP)


    一つだけやろう!


    関数は一つのことをしなければなりません.そのことをうまくやらなければならない.それしかできない.

  • どうして「一つのこと」が何なのか知ってるの?
  • で指定した関数名の下で、「抽象レベル」のステップが1つしか実行されない場合、その関数は1つの操作のみを実行します.

  • ただし、別の関数を意味のある名前で抽出できる場合は、複数の操作が実行されます.
  • '추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 하는 것이다.'
    라는 말이 어려울 수 있다. 하지만 예시를 보면 그렇게 어려운 것은 아니다.
    1. 쓰레기를 판단한다.
    2. 쓰레기를 줍는다.
    3. 쓰레기를 휴지통에 넣는다.	
    ごみをゴミ箱に捨てる関数の下で、3つの関数を呼び出す必要があることがわかります.したがって,関数名では抽象レベルは1つしかないと考えられる.つまり、1つのことしかしない関数は、関数中のすべての文の抽象レベル(判断、ピックアップ、挿入など)...表示は同じでなければなりません.
    NOT
    判断、ピックアップ、挿入の3つの関数で区別できます.すなわち,判定関数からピックアップに至るまで,判定やピックアップなどの抽象化を行うことはできない.

    抽象化レベルとは?


    その名の通り、具体的に書くのではなく抽象的に表現すれば、抽象化の程度が高く、抽象化ではなく、直接のコードは抽象化の程度が低い.

    public Money calculatePay(Employee e) throws InvalidEmployeeType {
    	switch (e.type) {
    		case COMMISSIONED:
    			return calculateCommissionedPay(e);
    		case HOURLY:
    			return calculateHourlyPay(e);
    		case SALARIED:
    			return calculateSalariedPay(e);
    		default:
    			throw new InvalidEmployeeType(e.type);
    	}
    }
    「計算してお金を生み出す.「2つの機能があります.」
    「新しい社員タイプを追加すると?」
    public abstract class Employee {
    	public abstract boolean isPayday();
    	public abstract Money calculatePay();
    	public abstract void deliverPay(Money pay);
    }
    
    public interface EmployeeFactory {
    	public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
    }
    
    public class EmployeeFactoryImpl implements EmployeeFactory {
    	public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
    		switch (r.type) {
    			case COMMISSIONED:
    				return new CommissionedEmployee(r);
    			case HOURLY:
    				return new SalariedEmployee(r);
    			case SALARIED:
    				return new SalariedEmployee(r);
    			default:
    				return new InvalidEmployeeType(r.type);
    		}
    	}
    }
    計算をタイプ管理から分離
    タイプの処理は最大限の工場でしかできません

    関数パラメータ


    因数の個数は0〜2個が好ましい.
    3つ以上なら?
    // 객체를 인자로 넘기기
    Circle makeCircle(double x, double y, double radius); // X
    Circle makeCircle(Point center, double radius); // O
    
    // 가변 인자를 넘기기 ==> 특별한 경우가 아니면 잘 안쓴다.
    String.format(String format, Object... args);

    セキュリティ関数の作成


    浮動小数点効果のない関数


    副手効果?値を返す関数は、外部ステータスを変更します.
    public class UserValidator {
    	private Cryptographer cryptographer;
    	public boolean checkPassword(String userName, String password) {
    		User user = UserGateway.findByName(userName);
    		if (user != User.NULL) {
    			String codedPhrase = user.getPhraseEncodedByPassword();
    			String phrase = cryptographer.decrypt(codedPhrase, password);
    			if ("Valid Password".equals(phrase)) {
    				Session.initialize();
    				return true;
    			}
    		}
    		return false;
    	}
    }

    リファレンス


    これらの情報はゼロベースラインクリーニングコード毎月1冊を聞いた後に整理された.