[インデックス 12353] ファイルの概要
このコミットは、Go言語の標準ライブラリnet
パッケージにおけるMACアドレスの解析ロジックを、既存のinterface.go
ファイルからmac.go
という独立したファイルに移動させるリファクタリングを目的としています。これにより、コードのモジュール性、保守性、および可読性が向上します。
コミット
commit 4d355836610a2c675f02cfcf75771504a4b69586
Author: Mikio Hara <mikioh.mikioh@gmail.com>
Date: Mon Mar 5 07:42:07 2012 +0900
net: move MAC address parser into distinct file
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5727054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4d355836610a2c675f02cfcf75771504a4b69586
元コミット内容
net: move MAC address parser into distinct file
このコミットは、MACアドレスの解析に関連するコードを、src/pkg/net/interface.go
からsrc/pkg/net/mac.go
という新しいファイルに移動させるものです。これに伴い、関連するテストコードもsrc/pkg/net/interface_test.go
からsrc/pkg/net/mac_test.go
に移動されています。
変更の背景
Go言語の標準ライブラリは、その設計原則として「シンプルさ」と「モジュール性」を重視しています。初期の段階では、関連する機能が単一のファイルにまとめられることがありますが、プロジェクトの成長とともに、特定の機能が肥大化したり、他の機能との依存関係が複雑になったりすることがあります。
このコミットの背景には、net
パッケージ内のinterface.go
ファイルが、ネットワークインターフェースに関する情報だけでなく、MACアドレスの解析という特定のロジックも含むようになっていたという状況があります。MACアドレスの解析は、ネットワークインターフェースの管理とは異なる、より汎用的なユーティリティ機能と見なすことができます。
このような状況でコードをリファクタリングする主な理由は以下の通りです。
- 関心の分離 (Separation of Concerns): MACアドレスの解析ロジックを独立したファイルに分離することで、
interface.go
は純粋にネットワークインターフェースの管理に特化し、mac.go
はMACアドレスの操作に特化します。これにより、各ファイルの責任が明確になり、コードの理解が容易になります。 - 保守性の向上: 特定の機能が独立したファイルにまとめられることで、その機能に関する変更やバグ修正が他のコードに与える影響を局所化できます。これにより、将来的なメンテナンスが容易になります。
- 再利用性の向上:
ParseMAC
のような汎用的な関数が独立したファイルに存在することで、将来的に他の場所でMACアドレスの解析が必要になった場合に、そのコードをより簡単に再利用できるようになります。 - テストの整理: 機能が分離されることで、テストコードもそれに対応して分離され、テストの構造がより論理的になります。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識があると役立ちます。
- Go言語のパッケージシステム: Go言語では、関連する機能がパッケージとしてまとめられます。パッケージはディレクトリ構造に対応し、他のパッケージからインポートして利用できます。
net
パッケージは、ネットワーク関連の機能を提供する標準ライブラリの一部です。 - MACアドレス (Media Access Control Address): ネットワーク上のデバイスを一意に識別するために使用される物理アドレスです。通常、
XX:XX:XX:XX:XX:XX
のような16進数表記で表されます。IEEE 802 MAC-48、EUI-48、EUI-64などの形式があります。 - リファクタリング: ソフトウェアの外部的な振る舞いを変更せずに、内部構造を改善するプロセスです。コードの可読性、保守性、拡張性を向上させることを目的とします。
bytes.Buffer
: Go言語の標準ライブラリbytes
パッケージに含まれる型で、可変長のバイトシーケンスを効率的に操作するためのバッファを提供します。文字列の構築などによく使用されます。fmt.Fprintf
: Go言語の標準ライブラリfmt
パッケージに含まれる関数で、フォーマットされた出力をio.Writer
に書き込みます。このコミットではbytes.Buffer
に書き込むために使用されています。errors.New
: Go言語の標準ライブラリerrors
パッケージに含まれる関数で、新しいエラー値を生成します。goto
文: Go言語におけるジャンプ文の一つで、指定されたラベルに処理を移します。現代のプログラミングでは、コードの可読性を損なう可能性があるため、goto
の使用は推奨されませんが、特定の最適化やエラーハンドリングのパターンで稀に使用されることがあります。このコミットでは、MACアドレスの解析におけるエラー処理のために使用されています。
技術的詳細
このコミットの技術的な詳細は、主にコードの移動とそれに伴う依存関係の調整にあります。
-
HardwareAddr
型と関連メソッドの移動:type HardwareAddr []byte
の定義。func (a HardwareAddr) String() string
メソッド(MACアドレスを文字列形式で表現)。 これらがinterface.go
からmac.go
へ移動しました。
-
ParseMAC
関数の移動:func ParseMAC(s string) (hw HardwareAddr, err error)
関数(文字列からMACアドレスを解析)。 この関数は、様々な形式のMACアドレス文字列(コロン区切り、ハイフン区切り、ドット区切りなど)を解析し、HardwareAddr
型に変換するロジックを含んでいます。解析に失敗した場合はエラーを返します。この関数もinterface.go
からmac.go
へ移動しました。
-
インポートの調整:
interface.go
からは、HardwareAddr
型やParseMAC
関数が不要になったため、それらに関連するbytes
やfmt
パッケージのインポートが削除されました。errors
パッケージは引き続き必要なので残されています。- 新しく作成された
mac.go
には、HardwareAddr.String()
で必要なbytes
とfmt
、そしてParseMAC
で必要なerrors
パッケージがインポートされています。
-
テストコードの移動と調整:
interface_test.go
からmactests
というテストデータ構造と、match
ヘルパー関数、そしてTestParseMAC
テスト関数が削除されました。- 新しく作成された
mac_test.go
には、これらのテストコードがそのまま移動されました。これにより、MACアドレス解析機能のテストが独立して実行できるようになります。テストコードはreflect.DeepEqual
やstrings.Contains
を使用して、解析結果とエラーが期待通りであるかを検証しています。
この変更は、Go言語の標準ライブラリにおけるコードベースの整理と、特定の機能の独立性を高めるための典型的なリファクタリングパターンを示しています。
コアとなるコードの変更箇所
src/pkg/net/interface.go
import
文から"bytes"
と"fmt"
が削除されました。HardwareAddr
型とそのString()
メソッドが削除されました。ParseMAC
関数が削除されました。
--- a/src/pkg/net/interface.go
+++ b/src/pkg/net/interface.go
@@ -6,11 +6,7 @@
package net
-import (
- "bytes"
- "errors"
- "fmt"
-)
+import "errors"
var (
errInvalidInterface = errors.New("net: invalid interface")
@@ -20,77 +16,6 @@ var (
errNoSuchMulticastInterface = errors.New("net: no such multicast interface")
)
-// A HardwareAddr represents a physical hardware address.
-type HardwareAddr []byte
-
-func (a HardwareAddr) String() string {
- var buf bytes.Buffer
- for i, b := range a {
- if i > 0 {
- buf.WriteByte(':')
- }
- fmt.Fprintf(&buf, "%02x", b)
- }
- return buf.String()
-}
-
-// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
-// following formats:
-// 01:23:45:67:89:ab
-// 01:23:45:67:89:ab:cd:ef
-// 01-23-45-67-89-ab
-// 01-23-45-67-89-ab-cd-ef
-// 0123.4567.89ab
-// 0123.4567.89ab.cdef
-func ParseMAC(s string) (hw HardwareAddr, err error) {
- if len(s) < 14 {
- goto error
- }
-
- if s[2] == ':' || s[2] == '-' {
- if (len(s)+1)%3 != 0 {
- goto error
- }
- n := (len(s) + 1) / 3
- if n != 6 && n != 8 {
- goto error
- }
- hw = make(HardwareAddr, n)
- for x, i := 0, 0; i < n; i++ {
- var ok bool
- if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
- goto error
- }
- x += 3
- }
- } else if s[4] == '.' {
- if (len(s)+1)%5 != 0 {
- goto error
- }
- n := 2 * (len(s) + 1) / 5
- if n != 6 && n != 8 {
- goto error
- }
- hw = make(HardwareAddr, n)
- for x, i := 0, 0; i < n; i += 2 {
- var ok bool
- if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
- goto error
- }
- if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
- goto error
- }
- x += 5
- }
- } else {
- goto error
- }
- return hw, nil
-
-error:
- return nil, errors.New("invalid MAC address: " + s)
-}
-
// Interface represents a mapping between network interface name
// and index. It also represents network interface facility
// information.
src/pkg/net/interface_test.go
import
文から"reflect"
と"strings"
が削除されました。mactests
テストデータ、match
ヘルパー関数、TestParseMAC
テスト関数が削除されました。
--- a/src/pkg/net/interface_test.go
+++ b/src/pkg/net/interface_test.go
@@ -6,8 +6,6 @@ package net
import (
"bytes"
- "reflect"
- "strings"
"testing"
)
@@ -96,46 +94,3 @@ func testMulticastAddrs(t *testing.T, ifmat []Addr) {
}
}
}\n-\n-var mactests = []struct {\n-\tin string\n-\tout HardwareAddr\n-\terr string\n-}{\n-\t{\"01:23:45:67:89:AB\", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, \"\"},\n-\t{\"01-23-45-67-89-AB\", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, \"\"},\n-\t{\"0123.4567.89AB\", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, \"\"},\n-\t{\"ab:cd:ef:AB:CD:EF\", HardwareAddr{0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}, \"\"},\n-\t{\"01.02.03.04.05.06\", nil, \"invalid MAC address\"},\n-\t{\"01:02:03:04:05:06:\", nil, \"invalid MAC address\"},\n-\t{\"x1:02:03:04:05:06\", nil, \"invalid MAC address\"},\n-\t{\"01002:03:04:05:06\", nil, \"invalid MAC address\"},\n-\t{\"01:02003:04:05:06\", nil, \"invalid MAC address\"},\n-\t{\"01:02:03004:05:06\", nil, \"invalid MAC address\"},\n-\t{\"01:02:03:04005:06\", nil, \"invalid MAC address\"},\n-\t{\"01:02:03:04:05006\", nil, \"invalid MAC address\"},\n-\t{\"01-02:03:04:05:06\", nil, \"invalid MAC address\"},\n-\t{\"01:02-03-04-05-06\", nil, \"invalid MAC address\"},\n-\t{\"0123:4567:89AF\", nil, \"invalid MAC address\"},\n-\t{\"0123-4567-89AF\", nil, \"invalid MAC address\"},\n-\t{\"01:23:45:67:89:AB:CD:EF\", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, \"\"},\n-\t{\"01-23-45-67-89-AB-CD-EF\", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, \"\"},\n-\t{\"0123.4567.89AB.CDEF\", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, \"\"},\n-}\n-\n-func match(err error, s string) bool {\n-\tif s == \"\" {\n-\t\treturn err == nil\n-\t}\n-\treturn err != nil && strings.Contains(err.Error(), s)\n-}\n-\n-func TestParseMAC(t *testing.T) {\n-\tfor _, tt := range mactests {\n-\t\tout, err := ParseMAC(tt.in)\n-\t\tif !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {\n-\t\t\tt.Errorf(\"ParseMAC(%q) = %v, %v, want %v, %v\", tt.in, out, err, tt.out,\n-\t\t\t\ttt.err)\n-\t\t}\n-\t}\n-}\
src/pkg/net/mac.go
(新規ファイル)
HardwareAddr
型とそのString()
メソッドが定義されました。ParseMAC
関数が定義されました。- 必要な
bytes
,errors
,fmt
パッケージがインポートされました。
--- /dev/null
+++ b/src/pkg/net/mac.go
@@ -0,0 +1,84 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// MAC address manipulations
+
+package net
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+)
+
+// A HardwareAddr represents a physical hardware address.
+type HardwareAddr []byte
+
+func (a HardwareAddr) String() string {
+ var buf bytes.Buffer
+ for i, b := range a {
+ if i > 0 {
+ buf.WriteByte(':')
+ }
+ fmt.Fprintf(&buf, "%02x", b)
+ }
+ return buf.String()
+}
+
+// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
+// following formats:
+// 01:23:45:67:89:ab
+// 01:23:45:67:89:ab:cd:ef
+// 01-23-45-67-89-ab
+// 01-23-45-67-89-ab-cd-ef
+// 0123.4567.89ab
+// 0123.4567.89ab.cdef
+func ParseMAC(s string) (hw HardwareAddr, err error) {
+ if len(s) < 14 {
+ goto error
+ }
+
+ if s[2] == ':' || s[2] == '-' {
+ if (len(s)+1)%3 != 0 {
+ goto error
+ }
+ n := (len(s) + 1) / 3
+ if n != 6 && n != 8 {
+ goto error
+ }
+ hw = make(HardwareAddr, n)
+ for x, i := 0, 0; i < n; i++ {
+ var ok bool
+ if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
+ goto error
+ }
+ x += 3
+ }
+ } else if s[4] == '.' {
+ if (len(s)+1)%5 != 0 {
+ goto error
+ }
+ n := 2 * (len(s) + 1) / 5
+ if n != 6 && n != 8 {
+ goto error
+ }
+ hw = make(HardwareAddr, n)
+ for x, i := 0, 0; i < n; i += 2 {
+ var ok bool
+ if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
+ goto error
+ }
+ if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
+ goto error
+ }
+ x += 5
+ }
+ } else {
+ goto error
+ }
+ return hw, nil
+
+error:
+ return nil, errors.New("invalid MAC address: " + s)
+}
src/pkg/net/mac_test.go
(新規ファイル)
mactests
テストデータ、match
ヘルパー関数、TestParseMAC
テスト関数が定義されました。- 必要な
reflect
,strings
,testing
パッケージがインポートされました。
--- /dev/null
+++ b/src/pkg/net/mac_test.go
@@ -0,0 +1,54 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+var mactests = []struct {
+ in string
+ out HardwareAddr
+ err string
+}{
+ {"01:23:45:67:89:AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+ {"01-23-45-67-89-AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+ {"0123.4567.89AB", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab}, ""},
+ {"ab:cd:ef:AB:CD:EF", HardwareAddr{0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}, ""},
+ {"01.02.03.04.05.06", nil, "invalid MAC address"},
+ {"01:02:03:04:05:06:", nil, "invalid MAC address"},
+ {"x1:02:03:04:05:06", nil, "invalid MAC address"},
+ {"01002:03:04:05:06", nil, "invalid MAC address"},
+ {"01:02003:04:05:06", nil, "invalid MAC address"},
+ {"01:02:03004:05:06", nil, "invalid MAC address"},
+ {"01:02:03:04005:06", nil, "invalid MAC address"},
+ {"01:02:03:04:05006", nil, "invalid MAC address"},
+ {"01-02:03:04:05:06", nil, "invalid MAC address"},
+ {"01:02-03-04-05-06", nil, "invalid MAC address"},
+ {"0123:4567:89AF", nil, "invalid MAC address"},
+ {"0123-4567-89AF", nil, "invalid MAC address"},
+ {"01:23:45:67:89:AB:CD:EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+ {"01-23-45-67-89-AB-CD-EF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+ {"0123.4567.89AB.CDEF", HardwareAddr{1, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}, ""},
+}
+
+func match(err error, s string) bool {
+ if s == "" {
+ return err == nil
+ }
+ return err != nil && strings.Contains(err.Error(), s)
+}
+
+func TestParseMAC(t *testing.T) {
+ for _, tt := range mactests {
+ out, err := ParseMAC(tt.in)
+ if !reflect.DeepEqual(out, tt.out) || !match(err, tt.err) {
+ t.Errorf("ParseMAC(%q) = %v, %v, want %v, %v", tt.in, out, err, tt.out,
+ tt.err)
+ }
+ }
+}
コアとなるコードの解説
このコミットのコアとなる変更は、MACアドレスの解析機能がinterface.go
からmac.go
へ完全に移動したことです。
HardwareAddr
型: これは[]byte
のエイリアスであり、MACアドレスのバイト表現を保持します。String()
メソッドは、このバイト列を一般的なMACアドレスの文字列形式(例:01:23:45:67:89:ab
)に変換するために使用されます。bytes.Buffer
を使用して効率的に文字列を構築しています。ParseMAC
関数: この関数は、入力された文字列s
を解析し、HardwareAddr
型に変換します。- フォーマットのサポート: IEEE 802 MAC-48、EUI-48、EUI-64の形式をサポートしており、コロン区切り (
:
), ハイフン区切り (-
), ドット区切り (.
) のいずれの形式にも対応しています。 - 長さのチェック: まず、入力文字列の長さがMACアドレスとして妥当な最小長(14文字)を満たしているかを確認します。
- 区切り文字による分岐: 文字列の3文字目(インデックス2)がコロンまたはハイフンであるか、あるいは5文字目(インデックス4)がドットであるかによって、解析ロジックを分岐させます。
- バイト変換: 各セグメント(例:
01
,23
)を16進数として解析し、バイト値に変換します。xtoi2
という内部ヘルパー関数(このコミットの差分には含まれていませんが、net
パッケージ内に存在するはずです)がこの変換を担当していると推測されます。 - エラーハンドリング: 解析中に無効なフォーマットや文字が検出された場合、
goto error
文を使用してエラー処理セクションにジャンプし、"invalid MAC address"
というエラーを返します。goto
の使用はGo言語では一般的ではありませんが、このようなエラーパスの早期終了には有効な場合があります。
- フォーマットのサポート: IEEE 802 MAC-48、EUI-48、EUI-64の形式をサポートしており、コロン区切り (
このリファクタリングにより、net
パッケージの内部構造がより整理され、各ファイルの役割が明確になりました。
関連リンク
- Go言語の
net
パッケージのドキュメント: https://pkg.go.dev/net - Go言語の
bytes
パッケージのドキュメント: https://pkg.go.dev/bytes - Go言語の
fmt
パッケージのドキュメント: https://pkg.go.dev/fmt - Go言語の
errors
パッケージのドキュメント: https://pkg.go.dev/errors - IEEE 802 MACアドレスに関するWikipedia記事: https://ja.wikipedia.org/wiki/MAC%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード(
src/pkg/net
ディレクトリ) - 一般的なソフトウェアリファクタリングの原則に関する情報