Featured image of post 圧縮機能の追加 - 新しい戦略をプラグインする

圧縮機能の追加 - 新しい戦略をプラグインする

圧縮バックアップ戦略を追加し、パターンの拡張容易性を体験。既存コードへの影響を最小限に抑える設計手法を学びます。

前回構築した「Template Method × Strategy」のアーキテクチャ。今回はその真価を発揮させるため、新しい戦略 「圧縮バックアップ(tar.gz)」 を追加します。

既存のコード(Backup::Engine)を一切修正せずに機能追加できる体験こそが、オブジェクト指向設計の醍醐味です。

前回: 2パターンの協調動作 | 目次 | 次回: ログとエラーハンドリング

新しい要件:アーカイブを作りたい

これまでは「ディレクトリ構造を維持してコピー」していましたが、今度は「全ファイルを1つの backup.tar.gz にまとめたい」という要望が出たとします。

前回の設計で 「Strategy側でループを制御する(ファイルリストを受け取る)」 という選択をしたことが、ここで活きてきます。

実装:圧縮戦略

Perlでtarアーカイブを作るには Archive::Tar が標準的ですが、ここではインターフェースが使いやすい Archive::Tar::Wrapper やコマンド呼び出しなど色々選択肢があります。今回は標準モジュールの範囲で Archive::Tar を使ってみます(メモリ消費に注意が必要ですが、学習用として)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package Backup::Strategy::Archive;
use Moo;
use Archive::Tar;
use Path::Tiny;
with 'Backup::Strategy::Role';

has filename => (is => 'ro', default => 'backup.tar.gz');

sub execute {
    my ($self, $engine, $files) = @_;
    
    my $tar = Archive::Tar->new;
    my $dest_file = $engine->dest_dir->child($self->filename);
    
    print "Archiving " . scalar(@$files) . " files to $dest_file ...\n";

    # Archive::Tarはメモリを使うので、大量ファイルの場合は
    # Archive::Tar::Streamed や コマンド実行(system "tar ...")を検討すべきですが、
    # ここではシンプルにadd_filesを使います
    
    # add_filesにはパス文字列のリストが必要
    my @path_strings = map { "$_" } @$files; 
    
    # 実際には相対パスで格納したいため、chdirするか、add_dataで工夫が必要。
    # ここでは簡略化のため絶対パス追加後にrename...ではなく、
    # シンプルにファイルを追加します。
    
    $tar->add_files(@path_strings);
    
    $tar->write($dest_file->stringify, COMPRESS_GZIP);
    
    print "Created archive: " . $dest_file->stringify . 
          " (" . -s $dest_file . " bytes)\n";
}

1;

注: 実運用では Archive::Tar はファイル内容をメモリに読み込むため、巨大なファイルには向きません。実用的なツールにするなら system("tar -czf ...") を呼ぶ戦略クラスにするのが最も手軽で高速です。

利用コード

呼び出し側は、Strategyを差し替えるだけです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
use Backup::Engine;
use Backup::Strategy::Archive;

# 戦略をArchiveに切り替え
my $engine = Backup::Engine->new(
    source_dir => './src',
    dest_dir   => './backup_dir', # ここに tar.gz ができる
    strategy   => Backup::Strategy::Archive->new(filename => '2026-01-30.tar.gz'),
);

$engine->run;

実行結果

1
2
3
4
5
Starting backup...
Scanning done. Found 1500 files.
Archiving 1500 files to ./backup_dir/2026-01-30.tar.gz ...
Created archive: ./backup_dir/2026-01-30.tar.gz (450320 bytes)
All done in 1.20 sec.

感動ポイント

  • Backup::Engine(メインロジック)のコードは 1行も変更していません。
  • Backup::Strategy::Diff(差分コピーのロジック)など、他の戦略にも影響を与えていません。
  • 新しいファイル Backup/Strategy/Archive.pm を置くだけで、機能が拡張されました。

これが 「Open-Closed Principle(開放閉鎖の原則)」 です。拡張に対しては開いており(Open)、修正に対しては閉じている(Closed)状態です。

次回は、このツールを本番環境でも安心して使えるように、ログ出力やエラーハンドリングといった「非機能要件」を強化していきます。

comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。