どうも、ねこきち(@nekokichi1_yos2)です。
リーダブルコード改善シリーズ、第2弾、です。
(詳しくは下記をご参照ください)
リーダブルコードで読みにくいコードを改善していく ~ 準備編
今回は変数や関数などの名前を改善していきます。
変な名前を見つける
ViewController
var list:Results<MemoObject>!
var text = [NSAttributedString]()
let realm = try! Realm()
let object = list[indexPath.row]
list・・・Realmのデータを受け取っているけど、何のリストなのかが不明
text・・・NSAttributedStringに変換したデータを格納しているが、何のためのテキストかが不明
realm・・・Realmのインスタンスだが、Realmのどの機能を担っているかが不明
object・・・取得したデータ群から取り出したデータを格納しているが、何のオブジェクトなのかが分からない
AddMemo
class MemoObject: Object {
@objc dynamic var data: Data!
@objc dynamic var identifier: String!
}
let picker = UIImagePickerController()
@IBAction func leftButton(_ sender: Any)
let memo: MemoObject = MemoObject()
let data = try! NSKeyedArchiver.archivedData(withRootObject: memoTextView.attributedText!, requiringSecureCoding: false)
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
let fullString = NSMutableAttributedString(attributedString: memoTextView.attributedText)
let pickerImage = image.resized(withPercentage: 0.1)!
let imageWidth = pickerImage.size.width
let padding: CGFloat = self.view.frame.width / 2
let scaleFactor = imageWidth / (memoTextView.frame.size.width - padding)
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(cgImage: pickerImage.cgImage!, scale: scaleFactor, orientation: pickerImage.imageOrientation)
let imageString = NSAttributedString(attachment: imageAttachment)
fullString.append(imageString)
memoTextView.attributedText = fullString
}
}
MemoObject・・・Realmに保存するためのモデルですが、
・どのようなオブジェクトなのか
・dataは何のデータを保存するのか
・identifierは何の識別子なのか
が明確でない。
leftButton・・・Realmにデータを保存する関数だが、そもそもNavigationBarの右側のボタンなのでleftは大間違いで、何の処理かが不明
memo・・・Realmに保存するのに使用する変数だが、メモなのはわかるが、何の用途に使うかの情報が足りない
data・・・Data型に変換されたtextViewの値を格納してるが、何のデータか分からない
picker・・・UIImagePickerだが、UIPickerViewなどと混同しそう
fullString・・・textViewの値をMutableAttributedStringに変換してますがAttributedStringだって情報は名前に含める方がいい
pickerImage・・・指定した値に圧縮(今回は10%に)してるが、圧縮後であることが分からず、こちらがピッカーで選択した画像だと勘違いする
imageWidth・・・pickerで選択した画像の幅だが、わざわざ変数にする必要はない
padding・・・textViewに表示する際の隙間だが、何のためのpaddingかが不明
scaleFactor・・・画像の拡大比率だが、Factorが表すのは何なのかが不明、そもそも比率だとぱっと見で判別できない
imageString・・・AttributedStringに変換した画像だが、imageとstringが連結してるだけで情報も役割も感じ得ない
DisplayMemo
var indexPath: Int!
var memo: MemoObject = MemoObject()
var text = NSAttributedString()
indexPath・・・ViewControllerで選択したindexPath.rowが格納されているが、rowを格納してるか分からず、ViewControllerから渡されたってことが明記されてな
memo・・・ViewControllerから渡されたメモのデータを格納しているが、Realmから取得したデータ、保存する用のデータ、などとごちゃ混ぜになって、どういうデータかが分からない
text・・・ViewControllerから渡されたattributedStringを受け取っているが、何の値でどこから受け取ったのかを明記すべき
EditMemo
extension UIImage {
func resized() -> UIImage
}
@IBAction func gesture()
@IBAction func complete()
resizedは、画像を圧縮するextensionですが、resizedだと過去形or過去分詞を表すので、resizeやresizingの方がいい
gestureは、長押しのジェスチャーで画像を添付する処理を持つが、どういうジェスチャーで何の処理をするのかが分からない
completeは、Realmに保存した編集したデータを保存するが、何が完了して、何をするのかが分からない
リーダブルコードの教え
「名前に情報を含めよ」
情報には、
・扱う値、処理の概要
・扱う値、処理の場所
・扱う値の単位
・時期、タイミング
が挙げられる。
情報を伝える要素として、
・名前の長さ
・接頭辞、接尾辞
・単語、キーワード
・英語の文法(名詞、動詞)
・関連する属性、状態
が挙げられる。
「誤解されない名前とは?」
・とにかく具体的である
・扱う値に応じた単位を用いている
・読み手の勘違いを回避
・Boolを扱う時、疑問形の並びになる
・他の似た処理とどう違うかが明確
・名前から値の中身を想像できる
「長い名前でも問題ない理由」
エディタの単語補完で頭文字を入力すれば、一瞬で呼び出せるから
「1つの単語やキーワードで良い場合とは?」
スコープが小さい場合、なぜなら、使用するor影響する変数や関数が近くにあるから
名前の改善する
ViewController
var memoList:Results<MemoModel>!
var attributedTextArray = [NSAttributedString]()
let realm = try! Realm()
let selectedMemoObject = memoList[indexPath.row]
memoList・・・複数のデータをRealmから取得するので、Listを追加
attributedTextArray・・・取得したデータのAttributedStringを取り出してるので、末尾をArrayに
realm・・・realmInstanceやrealmRefも考えたが、realmに関する変数は他にないので、realmだけで十分だと判断
selectedMemo・・・tableViewで選択されたデータを取り出し、Realmに1度は保存したデータなので、ObjectやDataなどの単語は省いた
AddMemo
class MemoModel: Object {
@objc dynamic var data: Data!
@objc dynamic var identifier: String!
}
let imagePicker = UIImagePickerController()
@IBAction func addMemo(_ sender: Any)
let memoObject: MemoModel = MemoModel()
let archivedAttributedText = try! NSKeyedArchiver.archivedData(withRootObject: memoTextView.attributedText!, requiringSecureCoding: false)
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickerImage = info[.originalImage] as? UIImage {
let mutAttrMemoText = NSMutableAttributedString(attributedString: memoTextView.attributedText)
let resizedImage = pickerImage.resizedImage(withPercentage: 0.1)!
let width = resizedImage.size.width
let padding: CGFloat = self.view.frame.width / 2
let scaleRate = width / (memoTextView.frame.size.width - padding)
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(cgImage: resizedImage.cgImage!, scale: scaleRate, orientation: resizedImage.imageOrientation)
let imageAttributedString = NSAttributedString(attachment: imageAttachment)
mutAttrMemoText.append(imageAttributedString)
memoTextView.attributedText = mutAttrMemoText
}
}
MemoModel・・・Realmに保存するためのモデルで、中の変数(data,identifier)はクラス名でメモのモデルだって明示してるので、名前を変更しないことにした。
imagePicker・・・UIImageのピッカーであることを示す。
addMemo・・・Realmにデータを追加するので、saveではなくaddをにした。
memoObject・・・Realmに保存する前の仮のデータなので、属性(Object)を含め、生成中or生成したデータであることを示す
archivedAttributedText・・・Data型としてアーカイブされたことを示す。
selectedImage・・・UIImagePickerで選択されたことを示す
mutAttrMemoText・・・NSMutableAttributedTextをmutAttrに略し、冗長にはせずに短くした
resizedImage・・・圧縮された画像なので、resizeを過去分詞の形にした
width・・・imagePickerControllerの中でしか使わず(スコープが小さい)、画像に使用すると理解できるので、他の情報は省いた
padding・・・widthと同様なので、他の情報は省いた
scaleRate・・・scale(拡大)Rate(割合)の意味を明確に表せる
imageAttachment・・・同じAttachmentでも画像なので、imageを頭につけた
imageAttributedString・・・画像を持つAttributedStringなので、頭にimageをつけた
DisplayMemo
var selectedMemoObject: MemoModel = MemoModel()
var selectedIndexPathRow: Int!
var selectedMemo_attributedText = NSAttributedString()
selectedMemoObject・・・他画面から渡されたデータであること、単体のデータであることから、属性(Object)を追加
selectedIndexPathRow・・・ViewControllerで選択されたことを示し、属性(Row)を含めて、中身が数字であることを明示
selectedMemo_attributedText・・・選択されたメモのattributedTextであることを示す
EditMemo
extension UIImage {
func resizeImage() -> UIImage
}
@IBAction func attachImageGesture()
@IBAction func updateMemo()
resizeImage・・・圧縮された画像を返すので、Imageを末尾に付けることで返り値であること示す
attachImageGesture・・・画像を添付するジェスチャーであり、textAttachmentを使用してるのでattachを使用
updateMemo・・・Realm上の該当するデータを更新するのでupdateにした
まとめ
本記事を執筆するまでは名前なんて直感で命名してましたが、たかが名前でも、変えるだけで可読性が上がるんだってようやく体感できました。
普通なら、とにかく開発を進めたくて、とりあえず扱うデータや処理に関連する単語をつなげるだけでは、わかりづらい名前になってしまいます。
しかし、
・その状況に適した具体的な情報
・いつ、何に、何を、何する、などの情報
が加わると、とてもわかりやすくなります。
また、下記の英語の文法を
・名詞
・動詞
・動詞の時期(現在進行形、過去形、未来形、過去分詞形など)
活用すれば、状態や時期などの細かい情報も含めることが可能です。
海外の記事やドキュメントを読んでいくうちに、英語は上達してくので、”英語やコードを読む”を実践し続ければ問題はないでしょう。
次回は、コードの美しさを改善していきます。