スーパー忙しい人向け
use config::config_file_path;
let path = config_file_path();
より
use config;
let path = config::file_path();
の方がいいよね
はじめに
タイトルは強い言葉を使っただけです。
刺さった人はぜひこの先をどうぞ。
どんな冗長であるコードが生まれたか
// ファイル分割をmodule blockで表現
// 実際は非推奨
mod paths {
use std::path::PathBuf;
// appそのものが配置されるディレクトリのパスを返す
fn app_local_dir_path() -> Option<PathBuf> {
...
}
// appのconfigが配置されるディレクトリのパスを返す
fn app_config_dir_path() -> Option<PathBuf> {
...
}
}
mod main {
use crate::paths::app_local_dir_path;
use crate::paths::app_config_dir_path;
use crate::config::config_init;
use crate::app::launch_app_from_local;
fn main() {
// 細かい部分は省略
// 冗長なコード!
let config_dir = app_config_dir_path().unwrap();
// appのコンフィグを初期化する関数のつもり
config_init(config_dir);
// 冗長なコード!
let dir_path = app_local_dir_path().unwrap();
// appをlocalから起動する関数のつもり
launch_app_from_local(dir_path);
}
}
私が過去に見てあまりの酷さに頭を抱えたコードです。(一般化済み & コメントは後付け)
このコードは当時動いてこそいましたが文字量で全体的にコードが読みにくくなっていた印象を受けました。
そうなった理由を挙げると、
- use句の多さ
- 関数の目的と処理を明確にしたいがあまり長い名前になっている
等があります。
paths
のみを例として提示しましたが、app
、config
の中身も同様であったと記憶しています。
理由として挙げた二つのうち、前者は規模が大きくなるにつれて避けられないリスクではあります。
ですが、避けられないリスクであるならば出来る限り軽減するべきだと私は思っています。
理由として挙げた際は"多さ"とだけ表現しましたが、もう少し正確に表現するならばそれはuse
する対象が関数であること、そしてその関数名が冗長なものでありファイルの上部を圧迫していることが理由であると言えます。
use
句自体はファイルの上部に固めて記述するのがスタンダードであり、それが圧迫されるのはファイルの可視性に問題が生じているということでしょう。
では、どうやって軽減するか。
それは単純に不要なuse
句を削除してファイル上部をコンパクトにすることです。
単純に削除とは言ってもその結果コンパイルが通らなくなっても問題なので、適宜修正する必要もあります。
use crate::{paths, config, app};
fn main() {
// 細かい部分は省略
// まだもう少し冗長なコード
let config_dir = paths::app_config_dir_path().unwrap();
// 冗長なコード
config::config_init(config_dir);
// まだもう少し冗長なコード
let dir_path = paths::app_local_dir_path().unwrap();
// 冗長なコード
app::launch_app_from_local(dir_path);
}
どうでしょうか。
関数の呼び出しをmodule::func
の形式にすることでuse
句を大幅に削減することが出来ました。
そして、形式を変更したことで新たに冗長な部分が見えてきたと思います。
冗長な理由として挙げたことの後者、
関数の目的と処理を明確にしたいがあまり長い名前になっている
です。
例を挙げると、config::config_init
やapp::launch_app_from_local
は同じ語句が二度使われていて冗長に感じることでしょう。
そして、形式を変更した以上その部分を削ることが出来ます。
use crate::{paths, config, app};
fn main() {
// 細かい部分は省略
// まだもう少し冗長なコード
let config_dir = paths::app_config_dir_path().unwrap();
// config_initから名称を変更
config::init(config_dir);
// まだもう少し冗長なコード
let dir_path = paths::app_local_dir_path().unwrap();
// launch_app_from_localから名称を変更
app::launch_from_local(dir_path);
}
ですが、この修正例ではpaths
に存在する関数を修正していません。
何故ならpaths
それそのものが冗長であるからです。
paths
そのものが冗長とはどういうことでしょうか。
そのことについて説明するためにはファイルの分割方法について多少知っておく必要があります。
上記の例ではmain
、config
、app
、paths
と四つにファイルが分割されています。
このうち、main
、config
、app
はそれぞれ単一の存在に対して作成され、関係しているファイルであると捉えることが出来ます。
ですが、paths
は各ファイルパスを返すという共通点がある関数の集合でしかなく、端的に表現するとこれは直接的な関係の無いものを似ているからというだけの理由で集めたものになります。
決してこの形態のファイルが悪であると断ずることは出来ませんが、その場合はmisc
などの名称として関係の無いものを集めたと明言した方がより正しいとは言えるであろうと考えています。
そして、そのような名称に変更せずとも大抵の場合はより良い位置に配置し直すことで解決すると共に冗長さも解決することが出来ます。
この場合、app_config_dir_path
はconfig
に、app_local_dir_path
はapp
に再配置することでpaths
自体を削除し、名称もそれに合わせて変更することで冗長さを排除することが出来ます。
use crate::{config, app};
fn main() {
// 細かい部分は省略
// app_config_dir_pathから名称を変更
let config_dir = config::dir_path().unwrap();
config::init(config_dir);
// app_local_dir_pathから名称を変更
let dir_path = app::local_dir_path().unwrap();
app::launch_from_local(dir_path);
}