こんばんは、final
警察です👮
既存のコードにfinal
をつけるのがめんどくさかったので、シェルスクリプトで一括final
をつけてみました。
Bashスクリプト
Bashわかんねーって言いながら、Mojaveまでデフォルトだった3.2でも動くように書いてみました。
#!/usr/bin/env bash TARGET_DIR="Sources/" while read result do RESULT=(${result//:/ }) FILE_PATH=${RESULT[0]} CLASS_NAME=${RESULT[2]} if [ `git grep -IE "class.+:.*[, ]$CLASS_NAME\W" $TARGET_DIR | wc -l` -eq 0 ]; then echo "Added 'final' to $CLASS_NAME." sed -i -E "s/^( *)class $CLASS_NAME/\1final class $CLASS_NAME/" $FILE_PATH fi done < <(git grep -I "^ *class [A-Z]" $TARGET_DIR)
ちょっといじればJavaとかにも使えると思います。
解説
git grep
git管理下の場合、普通のgrep
より速いです。
デフォルトで8スレッドで動いてくれます。
-I
でバイナリファイルを除いた検索ができます。
もっと速いgrep
も探すとあります。
class.+:.*[, ]$CLASS_NAME\W
継承しているクラスを探します。
$CLASS_NAME
がBase
の時に、
class Hoge: Base {
のような書き方にマッチします。
末尾の\W
でHoge2
のような部分一致のケースを弾いています。
Lintが入っている前提の、ある意味性善説的なマッチなので、
class Hoge:Base {
のようなイレギュラーなケースに対応する場合は、class.+:(.*[, ]){0,1}Base\W
みたいに書いておいたほうが安全です。
あと、typealias
を使って継承みたいな書き方をされると、対応できません。
while read ~ done < <(expr)
for
だとスペース入りの文字列がsplitされてしまうので、こう書いています。
なぜfinalをつけるか
保守性の文脈だと、Swiftでは基本的に継承は使わず、protocol
を使うことになってるはずなので、不用意な継承を避けるためにもfinal
をつけたほうが良いです。
その他の言語でも、大体コーディング規約などで推奨されているはず。(たとえば書籍だとEffective Java(3rd Edition)のItem 17とか)
Effective Java (English Edition)
- 作者:Joshua, Bloch
- 発売日: 2017/12/18
- メディア: Kindle版
とくにSwiftの場合、C++などと同じく、継承される可能性がなくなれば、実行時に実際に呼ばれるメソッドやプロパティを決めるDynamic Dispatchの必要がなくなるため、パフォーマンスが上がります。
Dynamic Dispatchの話はまた今度。
締め
厳密にやるなら、がっつりSwift Syntaxとかでやる感じでしょうか。
仕事しんどいし、腰の左あたりがめっちゃいたいので、しんどくなくなって忘れてなかったらやります。