Skip to content

lsコマンドをオブジェクト指向で作成しました#12

Open
s-tone-gs wants to merge 17 commits intomainfrom
ls_object
Open

lsコマンドをオブジェクト指向で作成しました#12
s-tone-gs wants to merge 17 commits intomainfrom
ls_object

Conversation

@s-tone-gs
Copy link
Copy Markdown
Owner

@s-tone-gs s-tone-gs commented Dec 3, 2025

FileLsMultiColumnLsLongFormatLsという4つのクラスを定義して作成しました。
testディレクトリ以下にはテストを記述したファイルとテスト用のファイルがあります。

レビューお願いいたします。

Copy link
Copy Markdown

@yoshitsugu yoshitsugu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントしました!

@@ -0,0 +1,61 @@
# frozen_string_literal: true

module My
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

あまり My という意味のないモジュール名は使わないほうがよいように思います。
たしかに、サンプルコードなどでは MyClass などの My という言葉がよく使われますが、これは「なんかなんでもいいので私がつくったクラス」くらいの意味で、サンプルコードとしての簡便さをとっただけであり、実務では使わないです。基本的に意味のある名前をつけましょう。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

安易な命名でした。
FileMetadataに修正しました。


attr_reader :name

private_constant :FILE_TYPES, :PERMISSIONS
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private_constant 使えていてよいですね 👍

opt.parse(ARGV)

files = get_files(all, reverse)
puts long_format ? LongFormatLs.generate(files) : MultiColumnLs.generate(files)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

フォーマットというのは出力制御の一種であり、それを元にコマンドやアプリケーション全体が分岐している、というのはちょっと違和感がありますね。
一般的に、プログラムは 入力 -> 演算 -> 出力 の3の部分で説明されることが多いです。この出力のところだけが全体部分に染み出してきているように見えます。
出力パーツが変化しうるもの、というくらいで捉えてはどうでしょうか。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラス構成を再考で修正しました。


require_relative 'file'

def get_files(all, reverse)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これだけ浮いているのもちょっと気になりますね。クラス設計は再度見直す必要がありそうです。
オブジェクト指向の設計で大事になってくるのは、何がコアとなるオブジェクトか、というところです。lsコマンドで一番重要な概念は何でしょう?たとえば、ファイルのメタデータ(ファイル名や作成日時など)をあつめたクラスをコアとして設計できそうではないでしょうか。
現状の My::File をたとえば FileMetadata などとリネームして、それをコアに据え、入力や出力は場合によって変わる、周辺的なもの、と考えましょう。そうすると

lsコマンドクラス
-> 入力を扱うクラス(今回だとオプションなど) -> FileMetadata
-> 出力を扱うクラス(今回だと標準出力) -> FileMetadata

みたいな依存関係にしておくと、FileMetadataは何にも依存していないので、コアとしてメンテしやすいクラスになりますし、もし出力がJSON形式になったとしても出力を扱うクラスを変更すればよいです。
このように考えてみてはどうでしょうか。

Copy link
Copy Markdown
Owner Author

@s-tone-gs s-tone-gs Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラス構成を再考で修正しました。

COLUMN_COUNT = 1
def initialize(files)
content_widths = calc_widths(files)
super(files, COLUMN_COUNT, content_widths)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ここでわざわざCOLUMN_COUNT=1でmatrixを使うのはかなり意味がないように思いますね。これもたぶん出力によって最初からわけてしまった影響だと思います。
表形式にしないといけないのは今回だと -l がない場合だけですよね。これは出力方式の関心事なので、 initialize で考慮しないといけない、というのはおかしい気がしますね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラス構成を再考にて修正しました。

Copy link
Copy Markdown

@yoshitsugu yoshitsugu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントしました

require 'optparse'
require_relative 'file'

class InputBuilder
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ちょっと名前が抽象的すぎるので、もう少し特定のものが想起されるようにしましょう。説明的ですがCommandLineArgumentsParserとかですかね。

Copy link
Copy Markdown
Owner Author

@s-tone-gs s-tone-gs Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラスの役割を見直しにて修正しました。
また、コマンドライン引数をパースした結果filesが返ってくるというのは不自然であったため、ファイル取得のメソッドをクラスから外しました。


class Ls
def self.run
files_and_option = InputBuilder.build
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

files, show_in_long_format = ...
みたいに分割代入するとわかりやすいように思います

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラスの役割を見直しにて修正しました。

def self.build
input_builder = new
all, reverse, long_format = input_builder.parse_options
files = FileMetadata.get_files(all, reverse)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

オプションとファイル取得までまとめたクラスを作るのであればこのクラス内にget_filesがあるほうがよい気がします。

Copy link
Copy Markdown
Owner Author

@s-tone-gs s-tone-gs Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#12 (comment)
こちらで命名を修正した際、そもそも命名が曖昧になったのも引数のパースとファイル取得という別の役割のものが一つのクラスにまとまっていることが原因であると考え、ファイル取得をクラスから外しました。

get_filesFileMetadataに持たせたまま、呼び出しをlsクラス内で行うようにしました。

@long_format ? LongFormat.generate(@files) : MultiColumnFormat.generate(@files)
end

class MultiColumnFormat
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラスごとにファイルを分けたほうが見やすいです。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クラスごとにファイルを分割にて修正しました。


def initialize(name)
@name = name
@state = ::File.stat(name)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

state ではなく stat ですね statistics の略だと思われます。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typoを修正
にて修正しました。
勘違いしておりました(;'∀')

Copy link
Copy Markdown

@yoshitsugu yoshitsugu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントしました!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ファイル名はクラス名と揃えましょう。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

修正しました。

opt.parse(ARGV)
[all, reverse, long_format]
end
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

結局オプションしか扱わない設計になったので、 CommandLineOptions くらいにしてもいいかなと思いました。
booleanの3つの値をそのまま返すと、自明でない順番に対する依存が発生します。最初がallというのはこのコードをちゃんと把握していないとわからないですよね。名前をつけたほうがよいです。
インスタンス変数で各オプションを持つようにして

def show_all?
  @all
end

などとすると

options = CommandLineOptions.new
options.show_all?

みたいにアクセスでき、よりわかりやすくなるかなと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require_relative 'multi_column_format'
require_relative 'long_format'

class DirectoryContentOutput
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lsコマンドの実際の組み込み版を考えると、 ls hoge.rb みたいにファイル指定しての表示もできるので、必ずしもDirectoryが対象であるとは限りません。
FileListPrinter とかになるかなと思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

出力クラスの命名を修正にて対応しました。

Copy link
Copy Markdown
Owner Author

@s-tone-gs s-tone-gs Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yoshitsugu
フィヨルドの通知が死んでいるのでこちらでもメンションさせていただきます。
お時間あるときに確認お願いします。

Copy link
Copy Markdown

@yoshitsugu yoshitsugu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OKです!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants