LoginSignup
10
17

More than 5 years have passed since last update.

もの作りをする時の考え方

Posted at

はじめに

(プログラムで)何かを新しく作る時に僕が考えている事になります。
今回はRubyスクリプトソースはこちら)を例に出しますが、Ruby以外でも参考になると思います。

※本記事はあなたのRailsアプリはいくらの解説編にもなってます。

作りたいものを考える

初めのステップとして必要な事は何を作るか?になります。
これは頑張って考えるというよりも、僕の場合は突然作りたくなるケースがほとんどです。

とりあえず作ってみる

作るものが決まったらとりあえず作ってみる事です。
プログラムの書き方が正しいかどうかは二の次で、どんな糞コードでも良いので作りましょう。

作ったものが形になった時には達成感を得られます。
また反省ができて、次のステップに進む事ができます。
うまく行けばサービスとして世の中に公開できます。

ディレクトリ構成を考える

プログラム初心者の人はとりあえず作ってみるとやってみても失敗する事の方が多いです。
そもそもモノが出来上がらなかったり、最初の一歩でつまづいてしまう事もよくあります。
また作ったプログラムを改修しようとしても、どこを直せば直るかわからないような酷いコードになっている事も多いです。
ただし、それでも一度自分一人でとりあえず作ってみるを行うことによって見えなかったものが見えてくるようになります。
ここから先を読む前に一度とりあえず作ってみるを実践してみると良いでしょう。

今回僕が作ったプログラムを元に作る時に必要になる構成を考えてみます。
このプログラムは簡単にいうと以下のようなフローを進んでいきます。

  1. how_much.rbがトリガーとなって動き出します。
  2. dispatch.rbがRailsプロジェクトからファイルを取り出します。
  3. 取り出されたファイルをprogram.rbが解析します。
  4. page.rbでHTMLを作成します。

このような一連の処理を行う上で重要となるのは、どこで何をさせるかです。

フレームワークを学ぶ

フレームワークは使う時にも役に立ちますが、何かを作る時の参考としても役に立ちます。
構成を考えるときにゼロベースで考えると新しいことを発見できる場合もありますが、大抵においては先人の知恵にはかないません。

そういった時には先人の知恵であるフレームワークを参考にすると良い構成を作りやすいです。

今回のプログラムを見てもらうとわかると思いますが、基本的な構成はRailsを参考にしてます。
appなどのディレクトリはないですが、Railsの構成をフラットにしていることがわかると思います。

このように分けておくことで、後々思ったよりも大きなプロジェクトに発展した時にある程度まで許容できるプログラムが出来上がります。

なので、まずは人が作った構成を真似てプログラムを配置するディレクトリ構造を考えましょう。
そして、間違ってても良いのでディレクトリ構成を先に作りましょう。

何が重要なプログラムになるか見極める

あなたのRailsアプリはいくらを作る時、真っ先に浮かべたのはDispatchのプログラムです。

このプログラムの性質上、ルートディレクトリから全てのディレクトリを舐めていく必要があります。
そのあとにファイル毎に金額を計算する必要があると思ったので、肝となるプログラムはDispatchクラスになると思ったのでこのプログラムを軸に構成を組み立てました。

今回のプログラムは1日足らずで作成した小さなものでしたが、大きなプログラムを作る時にも考え方は同じです。
肝となる部分がどこかを見極める事はプログラムを作る上で重要なポイントになります。

重要な部分は計算部分ではないのか?

今回のプログラムでいうとProgramクラスが中核を担っているようにも見えますが、このプログラムは呼び出されるクラスであるためdispatchとインターフェースを合わせれればどんな形でも動きます。

なので、僕はRailsプロジェクトを舐めていくdispatchの方が重要でありprogram部分はなんとでもなると判断しました。
もし、programから作った場合は、具体的なインターフェース部分がぼやけた状態で作成するため完成までもう少し時間がかかったでしょう。
(できない事はなかったと思います。)

ただし、プログラムが大きくなればなるほど重要なプログラムは増えていきます。
今回のように簡単にはいかないと思いますが、初めの一歩としては小さなプログラムの中で重要となるプログラムを見極める力を培うのは重要な事です。

ボトルネックを事前に調べておく

今回のような小さなプログラムではあまりボトルネックが見当たりませんでしたが、重要なプログラムを見極めるのと同様にボトルネックを見極める事も重要になります。

今回でいうと以下の部分がボトルネックになりそうだったので事前に軽く調べておきました。

スクリプトの引数を受け取る方法

僕はRubyといえばRailsでやってたので、スクリプトの引数を受け取る方法を知りませんでした。

ARGV[0]

$0でプログラム名も受け取れるようです。($1で第一引数を受け取れるという記事も見たのですが、僕が確認した限りでは受け取れれませんでした。)

ERBの使用可否

最後にHTMLを出力する際にヒアドキュメントで実装するのは厳しいと思ったので、事前に使えるかどうかだけ確認しました。
結果、使える事が分かったので調査はそこで終わらせてあとは実装時に検証して作りました。

require 'erb'

erb = ERB.new(File.read(Erbファイル))
# bindingは変数ではなくメソッドです。
p erb.result(binding)

requireに関して

Railsの場合はオートロードでファイルが読み込まれるので、自前でrequireを行うときはGemを使う場合などだけでした。
なので、事前に「どのようにファイルを読み込めるのか?」と「実行パスが違っても実行できるのか?」を調べておく必要がありました。

また、ディレクトリ内部のrbファイルをすべて読み込めるようにしておかないとrequireだらけになると思ったのでこちらも調べておきました。

些細な疑問

requireについてはRailsのプログラムが参考になったのですが、根深いところはわかってません。
その中で一つの疑問があります。

継承関係を持つクラスの場合は親クラスの方を先にrequireしておく必要があります。
しかし、Railsでは親クラスを先に読み込む指定をしなくても実行時エラーは発生しません。
なぜなんでしょうね・・・?

これらを事前に調べた理由

今回のプログラムは難しい事はなく、ボトルネックなど調べる必要はなかったのですが、、、1日で実装するつもりだったので、可能であるかどうかを知るために事前に調べておきました。

実際に現場でアーキテクチャーとして働く場合はボトルネックを先に調べないと、最後の最後に悲劇が待っている場合もあります。
(最後に実現不可能な事に気付き、泥臭い方法で回避するような局面を迎えるでしょう。)

一連の処理を作成する

ここまで出来たら一連の成功処理を作成しましょう!
イレギュラーなパターンは後で肉付けをしても問題ありません。各ディレクトリに一つしかファイルがなくても構いませんので一連の処理を作成します。

僕の場合はこのような形で一連の処理となる部分を作りました。

Railsプロジェクトを舐めて表示する

ここまでをまず作り、ファーストコミットとしました。
後はパーサー部分を作成すれば良いという状況に持っていきます。

Railsアプリでいうなら紙芝居(画面遷移だけで、処理は行わない状態)を作ったタイミングに似てます。

主要処理を作成する

ここまで出来たら主要処理の部分のインターフェースを考えて実装するだけです。
僕はProgramというクラスを作成して、このクラスを起点にサブクラスを作成することにしました。

このように起点となるクラスを作成すればインターフェースは自ずと決まってきます。
Dispatchから呼び出すクラスはProgramのサブクラスなので、できる限り処理をProgramクラスに記述します。

それによってサブクラスの負荷を下げるように設計します。(継承は開発者を楽にして、開発効率を上げた上に保守性も上げるという素晴らしい仕組みです。ぜひ活用していきましょう!)

このような形で主要処理が出来上がりました。

こんなバグも出してしまいましたが・・・。(恥

肉付けをしていく

最後は肉付けです。
初期の構想時点から値段をつけるファイルはrb以外にもcoffeescssなどというファイルが存在していることはわかってました。

なので、それらの部分をこのように肉付けしていきます。

若干ダルい部分ですが、このようなプログラムを書くケースは往々にしてあると思います。

この時点で係数にもいろいろ試行錯誤したことがわかると思います。

命名に関して

・・・。すいません。
本来ならこの部分は重要になるのですが、小さいプログラムだったため結構手を抜いてしまいました・・・。
命名に関してはあまり参考にしないでください・・・

大きなプロジェクトでは、命名にはそれなりに時間をかける必要があります。
後で見た時にこのプログラムが何をしているかがわかる名前をつけておくと肉付けをする時や改修をする時に楽になります。

ピックアップすべき技術

・・・という程のプルグラムは書いてないのですが、微細ながら書いておきます。

絶対パスの作成

自分のファイルを起点に絶対パスを取得することで、どこに配置されても動くコードができます。
ただし、自分自身の位置を移動させるとプログラムが動かなくなるのでご注意ください。

File.expand_path("../path", __FILE__)

__FILE__が自分自身の絶対パスを保持しています。
../としているのは自分自身のパスもディレクトリとして扱ってしまうためです。
../以下が自分自身のパスになります。

ディレクトリ内部のファイルロード

指定パスの配下に存在するファイル全てを読み込む。

  def self.path(path)
    Dir[File.expand_path("../../#{path}", __FILE__) << '/*.rb'].each do |file|
      require file
    end
  end

再起ロジックとコールバック

rubyでは&***と書いた引数はブロックを受け取れます。(&blockの名称が一般的)
そのブロックを使ってコールバックを行ってます。

またread_all_file内でread_all_fileを呼び出すことで、ディレクトリ内部のディレクトリを再帰的に探し出してます。
昨今では大した技術では無いのですが、昔はこの手の再起プログラムを書ける人は随分少なかったです。


  # 再帰的に全てのフォルダを読み込みます。
  def read_all_file(current_path = "", &block)
    # ディレクトリを読み込みます。
    Dir.foreach("#{@rails_path}/#{current_path}") do |file|
      # 不要なディレクトリはスキップ
      next if file == "." || file == ".."

      if FileTest::directory? "#{@rails_path}#{current_path}/#{file}"
        # 対象外のフォルダの場合はスキップする。
        next if AppConfig.get(:base, :ignore_dir).include?(file)

        # ディレクトリの場合は再起する。
        read_all_file("#{current_path}/#{file}") do |rails_path, file_name|
          # ファイルの場合はコールバックを呼び出す。
          block.call("#{rails_path}", file_name)
        end
      else
        # ファイルの場合はコールバックを呼び出す。
        block.call("#{current_path}", file)
      end
    end
  end

クラスの返却

Rubyではクラスも含めて全てがオブジェクトです。
なので、以下のようにクラスを返却して取得した先でインスタンスを作成することができます。
今回のケースでは無理に使う必要はなかったのですが、覚えておくと役に立つ時があります。

def hoge
    return HogeClass
end

hoge.new

インスタンス変数キャッシュ

以下のプログラムを見ることは多いと思います。

  def method_price
    @method_price ||= config(:method, :price)
  end

@method_priceはここ以外では使ってません。
なんのために行っているかというと、初回呼び出しでは@method_priceにデータを格納しますが2度目からは@method_priceに格納された値を使うテクニックです。

今回のケースではyamlの読み込みのキャッシュをしているのでほとんど効果は無いのですが、Railsを使う場合はActiveRecodeを扱う際に役に立ちます。
一度目はSQLを発行して、2度目からはキャッシュを使うことで高速化&リソースの有効活用ができます。

インスタンスメソッドの動的作成

このテクニックは今回初めて知ったのですが、各サブクラスで一部だけ変更して他の挙動は同じにしたい場合があると思います。
そういう時に以下のような事で動的にメソッドを作る事ができます。

  # 使用する設定ファイルの定義
  def self.config_file(symbol)
    define_method(:config_file) {
      @config ||= AppConfig.get(:program, symbol)
    }
  end
  config_file :base

このように記述してサブクラスで以下のように定義するとインスタンス化をする前にメソッドが作成されます。

config_file :ruby

Railsで有名なところだとbefore_actionなんかがこの手法で作られてます。
インスタンス化した後に値を設定したり、メソッドをオーバーライドしても良かったのですが・・・そうすると管理が面倒になったり行数が増えるためこの方法を採用しました。

結構便利です。
define_methodがミソです。

ちなみにRailsbefore_actionは以下のように定義されてます。

[:before, :after, :around].each do |callback|
        define_method "#{callback}_action" do |*names, &blk|
          _insert_callbacks(names, blk) do |name, options|
            set_callback(:process_action, callback, name, options)
          end
        end
        alias_method :"#{callback}_filter", :"#{callback}_action"

        define_method "prepend_#{callback}_action" do |*names, &blk|
          _insert_callbacks(names, blk) do |name, options|
            set_callback(:process_action, callback, name, options.merge(:prepend => true))
          end
        end
        alias_method :"prepend_#{callback}_filter", :"prepend_#{callback}_action"

        # Skip a before, after or around callback. See _insert_callbacks
        # for details on the allowed parameters.
        define_method "skip_#{callback}_action" do |*names|
          _insert_callbacks(names) do |name, options|
            skip_callback(:process_action, callback, name, options)
          end
        end
        alias_method :"skip_#{callback}_filter", :"skip_#{callback}_action"

        # *_action is the same as append_*_action
        alias_method :"append_#{callback}_action", :"#{callback}_action"
        alias_method :"append_#{callback}_filter", :"#{callback}_action"
      end

まとめ

いかがだったでしょうか?
参考になったかどうかわかりませんが、指針もなくアプリを作るといろいろ迷走をするのでちょっとでもお役に立てればと思います。

作った感想

作ってみるとプログラム規模が小さくて楽しく作れました。
最後の肉付けは少々面倒でしたが、適当に設定してもそれなりの金額が出るのでまぁいいかという感じで作ってみました。

僕が今作っているRailsアプリに適用したところそれなりの金額が算出されたので、やってみると面白いと思います。
複数のアプリに対して行うとプログラム規模が比較できるので、一つの指針になるのでは無いでしょうか?

余談

余談ですが、あなたのRailsアプリはいくらは思い立った翌日に開発着手〜完了となりました。
上記の事を約5時間ほどかけて行い、作成に至りました。

筆者について

僕はTownSoftの屋号を掲げている個人事業主です。
お仕事お引き受けしますので、お気軽にご連絡ください。

10
17
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
17