Goデザインパターン(2)-オブジェクト指向分析と設計

数日前、最初のデザインパターンGoデザインパターン(1)-文法を完成させましたこの記事では、主に、プロジェクトに遭遇した後のオブジェクト指向分析と設計の方法について説明します。この記事の多くのアイデアは Wang Zhengのデザインパターン美しさから生まれていますが私自身の経験とアイデアに基づいて説明します。

多くの学生がデザインパターンに関する本を読んでいますが、実際の仕事で使うのは難しいことが多いので、特定のビジネスをオブジェクト指向のプロセスに変える方法を考えていなかったことが理由のひとつかもしれません。このプロセスに慣れたら、プロセス指向の考え方からオブジェクト指向の考え方に移行し、学習した設計アイデアを使用してビジネスを設計および最適化できます。

ビジネス

勤勉なプログラマーとして、ある日、リーダーは次のように語っています。「インターフェース呼び出しのセキュリティを確保するために、インターフェース呼び出し認証機能を設計および実装したいと考えています。認証されたシステムのみがインターフェースを呼び出すことができます。認証されていません。インターフェースへの呼び出しは拒否されます。このタスクの開発に責任を持ち、できるだけ早くオンラインになるよう努めてください。」

オブジェクト指向分析(OOA)

オブジェクト指向分析の出力は、詳細な要件の説明です。

基本分析の最初のラウンド

分析の最初のラウンドは、主に特定のニーズが何であるか、そしてそれらを解決するためにどのような技術的ソリューションを採用できるかを分析することです。

リーダーと話し合った後、私のグループが管理しているバックグラウンドシステムは、他のグループが呼び出すためのいくつかのAPIインターフェースを提供する必要があることがわかりました。これらのインターフェースによって返されるデータは機密性が高く、セキュリティを確認する必要があります。

突然この問題に遭遇するのは少し恥ずかしいかもしれません。どのような選択肢があるかを考えることができます。アイデアがない場合は、同僚と話し合うことができます。また、チームが以前にどのように行動したかを確認したり、インターネットで通常の解決策を検索したりすることもできます。 。

社内で一般的に使用されているAPI認証スキームはX5プロトコルです。オンライン情報を表示するには、署名スキームが一般的に使用されます。

X5プロトコルの例:

appidとappkey
  • appidそしてappkey、使用する前に管理者に申請してください
  • appkey計算にのみ使用され、漏れがあればいつでも交換できます。
インターフェイス送信データ$ sendData形式の定義:
<?php
array(
    'header'=>array(
            'appid'=>$appid,
            'url'=>$url,
            'method'=>$method,
            'sign'=>$sign,
        ),
        'body'=>$body,
);
?>
サインフィールドアルゴリズム

appid対応するインタフェース要求メッセージJSON文字列がされappkey次々にスプライスされ、32ビットのMD5は、それ上で実行され、その後、大文字に変換

$sign=strtoupper(md5($appid.$reqBody.$appkey))
スプライシング要求メッセージ

既存のパラメーターを次の形式でスプライスし、base64でエンコードして最終的なリクエストボディを取得します。

$encRequest=base64_encode(json_encode($reqStream));

現在の状況に応じて、認証を実装するためにX5プロトコルが選択されています。これは主に社内契約であるため、各グループは比較的精通しており、将来的には比較的簡単に接続、調整、拡張できるようになります。

分析と最適化の第2ラウンド

このラウンドでは、技術計画を改善し、フローチャートを作成する必要があります。

比較的簡単な解決策は、プロセス全体を実行してから、プロセスの詳細について質問することです。

  1. 発信者はappidとappkeyを申請します
  2. 呼び出し元はappidとappkeyを使用して符号を計算し、base64でリクエストパラメーターをエンコードします
  3. 発信者はサーバーにリクエストを送信します
  4. サーバーはデータをデコードし、符号を再計算し、符号が等しいかどうかを判断します

プロセスを注意深く分析するには、次の質問を検討する必要があります。

  1. appidとappkeyを申請して保存する方法
    • 発信者の数が増えるにつれて、どのappidがどのグループに割り当てられているかを確認する必要があります
    • appidとappkeyは簡単に変更でき、発信者とサービスを同時に有効にするのが最適です。
    • APIごとに異なるappidとappkeyを設定する必要がありますか
  2. 多くのシステムまたはインターフェースには認証要件があります
    • より普遍的でビジネスへの影響を最小限に抑えるように設計する方法
    • 他のシステムで使用するSDKを提供する必要がありますか
  3. リプレイ攻撃を考慮する必要がありますか

このように考えると、最も包括的な方法で行うと、ワークロードは依然としてかなり大きくなります。実情を踏まえて、まずはシンプルバージョンを完成させ、取引量が増えた時点でフルバージョンを最適化することができます。したがって、上記の質問に対して、次の選択を行います

  1. appidとappkeyはサーバー構成ファイルに保存され、呼び出し元は適用されたappidとappkeyを維持し、システムのディメンションに従ってappidとappkeyを提供します。後で、構成センター、ETCD、またはRedisを使用して、appidとappkeyを管理できます。
  2. 今回は、システムでのみ使用する必要がありますが、設計は普遍的である必要があり、サービスへの侵入は少ないです。発信者数が増えたら、SDKを提供できます。
  3. それらはすべてイントラネットから呼び出されるため、比較的安全であり、リプレイ攻撃は最初に考慮されません。

フローチャート

キーレビュー

要件がより抽象的で曖昧な場合があり、明確で達成可能な要件定義を作成するには、自分で掘り下げてトレードオフを行う必要があります。要件分析は反復プロセスであり、一般的な計画を立てて、それを段階的に改善することができます。このプロセスでは、最初に一般的なプロセスをリストし、次にさらに質問をし、これらの質問にさらに答えることができ、最後に出力が必要です。

数日前、クラスメートが同様の仕事をするように手配されたと言うのも興味深いですが、それは汎用性とスケーラビリティの問題について考えるのではなく、タスクを完了することだけでした。実際、理解しやすく、前任者がやったことを模倣したものを書くのははるかに簡単ですが、このように能力とシステムアーキテクチャを改善することは困難です。また、今後のプロジェクトに取り組む際には、もっと考えていただければ幸いです。

オブジェクト指向設計

オブジェクト指向設計の出力はクラスです。

責任を分割し、タイプを特定します

このステップを達成するために、要件に従って関連するファンクションポイントを1つずつ説明し、次に、どのファンクションポイントが同様の責任を持ち、同じ属性を操作するかを確認できます。それらは同じカテゴリに分類できますか。

ファンクションポイントリスト:

  1. appidとappkeyを使用してリクエストパラメータを暗号化し、符号を生成します
  2. Base64は要求されたデータをエンコードします
  3. base64を使用してリクエストデータをデコードし、appidを取得します
  4. appidに対応するappkeyをストレージから取得します
  5. 符号を再計算して、2つの符号が一致するかどうかを判断します

1、5は符号に関連し、2、3はエンコードと解析に関連し、4はストレージに関連します。したがって、3つのコアクラスを大まかに取得できます。AuthToken、ApiRequest、CredentialStorage。AuthTokenは2つの操作1、5を担当し、ApiRequestは2つの操作2、3を担当し、CredentialStorageは4つの操作を担当します。

もちろん、これはクラスの予備的な分類です。当面、他の重要でないコーナーアンドコーナークラスについて考えることはできないかもしれませんが、それは問題ではありません。オブジェクト指向分析、設計、およびプログラミングは元々です。サイクル。反復的かつ継続的な最適化プロセス。

クラスとその属性およびメソッドを定義します

メソッドの識別では、要件の説明にある動詞が候補メソッドとして識別され、さらにフィルタリングされます。属性の識別には、ファンクションポイントに含まれる名詞が候補属性として使用され、同じフィルタリングが実行されます。

次に、各クラスの属性とメソッドを見てみましょう。まだファンクションポイントのリストから掘り下げています。

AuthToken

AuthTokenクラスに関連する2つの関数があります。

  1. appidとappkeyを使用してリクエストパラメータを暗号化し、符号を生成します
  2. 2つの記号が一致するかどうかを判断します

動詞は次のとおりです。生成、一致

名詞は次のとおりです。sign(appidとappkeyは、ビジネスの観点からAuthTokenに属していないため、パラメーターとして渡すことができます)

type Header struct {
    
    
	AppId string
	Sign  string
}
type Data struct {
    
    
	Header Header
	Body   string
}

type AuthToken struct {
    
    
	sign string
}

func CreateAuthToken() *AuthToken {
    
    
	return &AuthToken{
    
    }
}

func (authToken *AuthToken) Create(appId string, appKey string, body string) string {
    
    
	h := md5.New()
	h.Write([]byte(appId + body + appKey))
	authToken.sign = strings.ToUpper(fmt.Sprintf("%x", h.Sum(nil)))
	return authToken.sign
}

func (authToken *AuthToken) Match(token *AuthToken) bool {
    
    
	if authToken.sign == token.sign {
    
    
		return true
	}
	return false
}

ApiRequest

ApiRequestクラスには2つの関連するファンクションポイントがあります。

  1. base64エンコーディングを実行してリクエストデータを生成します
  2. base64デコードを実行し、appidを取得して署名します

動詞は次のとおりです:エンコード、デコード

type ApiRequest struct {
    
    
	appId string
	sign  string
	data  *Data
}

func CreateApiRequest() *ApiRequest {
    
    
	return &ApiRequest{
    
    }
}
func (apiRequest *ApiRequest) Encode(data string) string {
    
    
	return base64.StdEncoding.EncodeToString([]byte(data))
}
func (apiRequest *ApiRequest) Decode(data string) (appId string, sign string, err error) {
    
    
	bytes, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
    
    
		return
	}
	apiRequest.data = &Data{
    
    }
	if err := json.Unmarshal(bytes, apiRequest.data); err != nil {
    
    
		return "", "", err
	}
	apiRequest.appId = apiRequest.data.Header.AppId
	apiRequest.sign = apiRequest.data.Header.Sign
	return apiRequest.appId, apiRequest.sign, nil
}

func (apiRequest *ApiRequest) GetAppid() string {
    
    
	return apiRequest.appId
}

func (apiRequest *ApiRequest) GetSign() string {
    
    
	return apiRequest.sign
}

CredentialStorage

CredentialStorageには、関連するファンクションポイントが1つだけあります。

  1. appidに対応するappkeyをストレージから取得します

データの場所は後で変更されるため、特定の実装プログラミングではなく、インターフェイスに基づいてインターフェイスとして設計することをお勧めします。

type CredentialStorage interface {
    
    
	GetAppkeyByAppid(appId string) string
}

type CredentialStorageConfig struct {
    
    
}

func (config *CredentialStorageConfig) GetAppkeyByAppid(appId string) string {
    
    
	if appId == "test" {
    
    
		return "test"
	}
	return "test"
}

クラス間の相互作用を定義する

クラス間の相互作用は、一般化、実装、構成、依存関係の4つのタイプに簡略化できます。

一般化:継承関係として理解することができます

実装:通常、インターフェース実装クラス間の関係を指します

組み合わせ:集約、組み合わせ、関連付けなどを含み、通常、他のカテゴリを含むカテゴリを指します

依存関係:2つのクラスに何らかの関係がある限り、それらは依存関係と見なされます

したがって、CredentialStorageとCredentialStorageConfigは実装関係です。

クラスをアセンブルし、実行エントリを提供します

次に、コード全体を実行するためにクラスをアセンブルする必要があります。

すべての実装の詳細をカプセル化し、トップレベルのApiAuthencatorクラスを設計し、認証ロジックの実行をトリガーするための入り口として、外部呼び出し元のAPIインターフェイスのセットを公開しました。

type ApiAuthencator struct {
    
    
	credentialStorage CredentialStorage
}

func CreateApiAuthenCator(cs CredentialStorage) *ApiAuthencator {
    
    
	return &ApiAuthencator{
    
    credentialStorage: cs}
}

func (apiAuthencator *ApiAuthencator) Auth(data string) (bool, error) {
    
    
	//1.解析数据
	apiRequest := CreateApiRequest()
	appId, sign, err := apiRequest.Decode(data)
	//fmt.Println(appId, sign, apiRequest.data)
	if err != nil {
    
    
		return false, fmt.Errorf("Decode failed")
	}
	//2.获取appId对应的appkey
	appKey := apiAuthencator.credentialStorage.GetAppkeyByAppid(appId)
	//3.重新计算sign
	authToken := CreateAuthToken()
	newSign := authToken.Create(appId, appKey, apiRequest.data.Body)
	if sign == newSign {
    
    
		return true, nil
	}
	return false, nil
}

オブジェクト指向プログラミング

オブジェクト指向設計が完了した後、クラス、属性、メソッド、およびクラス間の相互作用を明確に定義し、すべてのクラスをアセンブルして、統一された実行エントリを提供します。次に、オブジェクト指向プログラミングの仕事は、これらの設計アイデアをコード実装に変換することです。

オブジェクト指向分析では、コーディング作業の一部が完了します。コードは、オブジェクト指向プログラミングの過程で完成させる必要があります。ただし、このビジネスは比較的単純なので、実行効果を確認するためにmain関数のみを記述します。

func main() {
    
    
   //客户端
   appId := "test"
   appKey := "test"
   sendData := &Data{
    
    
      Header: Header{
    
    
         AppId: appId,
      },
      Body: "for test",
   }
   authToken := CreateAuthToken()
   sign := authToken.Create(appId, appKey, sendData.Body)
   sendData.Header.Sign = sign
   sendDataMarshal, _ := json.Marshal(sendData)
   sendDataString := CreateApiRequest().Encode(string(sendDataMarshal))
   //fmt.Println(sign, sendData, string(sendDataMarshal), string(sendDataString))

   //服务端
   apiAuthenCator := CreateApiAuthenCator(new(CredentialStorageConfig))
   auth, err := apiAuthenCator.Auth(sendDataString)
   if err != nil {
    
    
      fmt.Println(err.Error())
      return
   }
   if auth == false {
    
    
      fmt.Println("auth failed")
      return
   }
   fmt.Println("auth success")
   return
}

利点

オブジェクト指向プログラミングプロセスの使用はプロセス指向よりも責任がありますが、ルーチンによれば、設計の経験があまりないジュニアエンジニアでも、このプロセスを段階的に実行して分析、設計、および実装を行うことができます。

オブジェクト指向の最も重要な点は、拡張ポイントを事前に準備することができ、将来変更があった場合に変更する必要があるコードはごくわずかであるということです。

オブジェクト指向設計は最良ではなく、優れているだけであり、開発に基づいた継続的な反復と再構築のプロセスです。

認証要件の場合、最も可能性の高い変更はアプリキーの取得方法ですが、インターフェイスが使用されるため、後の変更では、アプリキーを取得するための新しいクラスを記述してから、CreateApiAuthenCator(new(CredentialStorageConfig))コードを変更するだけで済みます。

うまく使用するためのオブジェクト指向

  1. ビジネスに精通し、将来何が変わるかを予測できる必要があります
  2. 適切な拡張ポイントを埋めるには、いくつかの原則とデザインパターンを理解する必要があります

総括する

長年の実務経験から、コーディングプロセスでオブジェクト指向の考え方をうまく使用する必要があることがわかります。そうしないと、システムがますます肥大化し、保守が難しくなります。この記事では、オブジェクト指向メソッドを使用するルーチンについて説明し、このルーチンに従い、常に自分自身を向上させてより良い人間になるようにします。

完全なコードを表示できます:https://github.com/shidawuhen/asap/blob/master/controller/design/2design.go

データ

  1. デザインパターンの美しさ
  2. インターフェイス認証

やっと

私の記事が気に入ったら、私の公式アカウント(プログラマーMala Tang)をフォローしてください。

私の個人的なブログは次のとおりです:https://shidawuhen.github.io/

以前の記事のレビュー:

  1. 言語に行く
  2. MySQL / Redis
  3. アルゴリズム
  4. アーキテクチャ/ネットワーク/プロジェクト
  5. 思考/メモを読む

おすすめ

転載: blog.csdn.net/shida219/article/details/113809307