Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Android開発における、レガシーコードと戦うためのリファクタリング
Search
Jumpei Yamamoto
August 20, 2015
Programming
3
1.7k
Android開発における、レガシーコードと戦うためのリファクタリング
2015/8/20 マネーフォワード×Eight -モバイルアプリ勉強会
Android開発におけるリファクタリングの実際を紹介します。
Jumpei Yamamoto
August 20, 2015
Tweet
Share
More Decks by Jumpei Yamamoto
See All by Jumpei Yamamoto
KotlinでDSLを作る #Kotlin_Sansan
boohbah
2
1.6k
みんな大好き拡張関数 #kotlin_sansan
boohbah
1
9k
sealed class in Kotlin1.1
boohbah
1
1.7k
Sansanではたらくアプリエンジニアの20%ルール
boohbah
0
730
KotlinでDSL
boohbah
0
9.1k
ObservableArrayとPikkel
boohbah
2
1.3k
KotlinでPhantom Type #kotlin_sansan
boohbah
2
3.8k
#jkug Kotlinのclass delegation
boohbah
1
340
#DroidKaigi 既存のAndroidプロジェクトに Kotlinを導入した話
boohbah
5
2.6k
Other Decks in Programming
See All in Programming
Honoとフロントエンドの 型安全性について
yodaka
7
1.5k
CDK開発におけるコーディング規約の運用
yamanashi_ren01
2
260
ソフトウェアエンジニアの成長
masuda220
PRO
12
2.2k
Serverless Rust: Your Low-Risk Entry Point to Rust in Production (and the benefits are huge)
lmammino
1
160
Kotlinの開発でも AIをいい感じに使いたい / Making the Most of AI in Kotlin Development
kohii00
5
1.9k
未経験でSRE、はじめました! 組織を支える役割と軌跡
curekoshimizu
1
200
責務と認知負荷を整える! 抽象レベルを意識した関心の分離
yahiru
8
1.5k
技術を改善し続ける
gumioji
0
180
Swift Testingのモチベを上げたい
stoticdev
2
200
CloudNativePGを布教したい
nnaka2992
0
120
もう少しテストを書きたいんじゃ〜 #phpstudy
o0h
PRO
20
4.3k
Expoによるアプリ開発の現在地とReact Server Componentsが切り開く未来
yukukotani
1
210
Featured
See All Featured
Done Done
chrislema
182
16k
Rebuilding a faster, lazier Slack
samanthasiow
80
8.9k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
30
4.6k
Site-Speed That Sticks
csswizardry
4
420
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
13
1k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Visualization
eitanlees
146
15k
4 Signs Your Business is Dying
shpigford
183
22k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
46
2.4k
Building Flexible Design Systems
yeseniaperezcruz
328
38k
Transcript
レガシーコードと 戦うためのリファクタリング 2015/8/20 マネーフォワード×Eight -‐‑‒モバイルアプリ勉強会-‐‑‒ Sansan株式会社 Eight事業部
⼭山本純平
⾃自⼰己紹介 ⼭山本純平 Sansan株式会社 Eight事業部 Androidアプリやってます 2015年年6⽉月⼊入社
趣味はギター ソウルミュージック好き @boohbah
前職では ガラケーのネイティブアプリ (メーラーとか) スマートフォンの プラットフォーム開発
ウェブポ Flashの年年賀状サービス Windowsの WPFを使った 写真アプリ
scene という写真アプリの iOS版を作ったりしていました
None
Eightは この7⽉月に全⾯面リニューアル 名刺刺管理理アプリからビジネスネットワーキングサービスへ進化
6⽉月から本格的な開発が始まり かなりタイトなスケジュール そんな中にEightの開発にJoinしたのですが…
前任者が退職 引き継ぎ期間は2⽇日 資料料はほとんどなし
引き継ぎ1⽇日⽬目 • まずは残タスクの洗い出し • スケジュールの確認
残りはペアプロ 仕掛中のタスクを丸⼀一⽇日⼀一緒に実装 開発環境の整備 設計・実装内容の確認 気になる実装に関する経緯 これが最も効率率率的に情報を引き出す⼿手段
※引き継ぎ資料料がない場合
さて本題
レガシーコードと 戦うためのリファクタリング
タイトなスケジュールの中 なぜリファクタリングか?
–Martin Fowler ʮϦϑΝΫλϦϯάʯ “リファクタリングはソフトウェアを 理理解しやすくする”
複雑なコードを理理解する 理理解した結果を後に残すことができる 既存のコードを理理解しながら開発を進めるにあたって リファクタリングがもっとも効率率率よく進められる⽅方法
今⽇日紹介する リファクタリングの実際 • 名前をつける • FatなActivityと戦う
名前をつける
public void onLoadFinished(Integer result) { switch (result) { case 10:
showErrorAlertDialog(); break; case 11: showErrorAlertDialog(); break; case 12: showErrorAlertDialog(); break; case 20: case 30: case 31: showErrorAlertDialog(); break; case 32: showErrorAlertDialog(); break; case 1: break; } } 何をあらわすのか わかりません!! 問題点
public enum FirstMyCardEntryStatus { /// ໊ը૾ߘཁٻʢະߘঢ়ଶʣ BEFORE_UPLOAD(10), /// ໊ը૾࠶ߘཁٻʢະߘঢ়ଶʣ REQUEST_REUPLOAD(11),
/// αϜωΠϧԽલঢ়ଶ BEFORE_THUMBNAIL(20), /// ໊ը૾ΤϯτϦૹ৴લ(໊ը૾ΤϯτϦొલ) BEFORE_SEND_ENTRY(30), /// ໊ը૾ΤϯτϦૹ৴ࡁΈ(໊ը૾ΤϯτϦొྃ) SENT_ENTRY(31), /// ໊ը૾ΤϯτϦड৴ࡁΈ(໊ը૾ೖྗྃ) FINISHED_ENTRY(32), /// ໊ը૾ΤϯτϦ࠷ॳͷϓϩϑΟʔϧͱͯ͠ηοτࡁΈ SET_AS_FIRST_PROFILE(40); private final int mStatusCode; FirstMyCardEntryStatus(int statusCode) { mStatusCode = statusCode; } public static FirstMyCardEntryStatus of(int code) { for (FirstMyCardEntryStatus e : FirstMyCardEntryStatus.values()) { if (code == e.mStatusCode) { return e; } } throw new IllegalArgumentException(); } public int getStatusCode() { return mStatusCode; } public boolean isAuthorized() { return this == SET_AS_FIRST_PROFILE; } } enumを使う 条件判定にも名前を つけられる
統⼀一した名前をつける
運営からのお知らせ ServiceMessage ConductNews FeedAnnouncement 問題点 同じ機能でも実装箇所によって 違う名前がついているため
コードがわかりにくい
統⼀一した名前 ユビキタス⾔言語 単語帳
実はこれまでも プロジェクト内単語帳は存在 ⽇日本語としては共通の単語の コンセンサスはある程度度のとれていた プログラム上の機能名(英語)は 開発者のセンスによる
⽇日本語を英語に訳す段階で ⽤用語がバラバラになってしまっていた
ユビキタス⾔言語には英訳も必要!! iOS -‐‑‒ Androidで命名を合わせるようにコミュニ ケーションをとる ソースコード上もわかりやすい名前に統⼀一
Fatな Activity/Fragmentと戦う
問題点 • 2000⾏行行超えるActivity / Fragment • メンテナンス、変更更が⾮非常に困難
Activityでは何をするべきか?
ドメイン駆動設計のレイヤアーキテクチャ Presentation Layer 7JFX 1SFTFOUFS Domain Layer 6TFDBTF Infrastructure Layer
Entity %# /FUXPSL Application Layer Activity アプリケーションレイヤ アプリケーションの活動を 調整する薄いレイヤ。 ビジネスロジックを含まな い。また、ビジネスオブジェ クトの状態を保持しない。 しかし、アプリケーション の処理理の進み具合を保持す ることがある。
Activityは ライフサイクルの管理理と 画⾯面遷移のハンドリング に集中するべき
実際は… Presentation Layer 7JFX Presenter Domain Layer Use-‐‑‒case Infrastructure Layer
Entity %# /FUXPSL Application Layer Activity Presenter, Use-‐‑‒case モジュールが存在せず その実装がすべて Activity/Fragmentに 含まれていた Activity
Activityを軽量量化する Presentation Layer 7JFX Presenter Domain Layer Use-‐‑‒case Infrastructure Layer
Entity %# /FUXPSL Application Layer Activity どうやってUse-‐‑‒case部 分を切切り出すか? Activity どうやってPresenter 部分を切切り出すか?
Activityから Use-‐‑‒caseを切切り出す
例例) メッセージをやりとりする画⾯面 メッセージを投稿する 定期的にサーバーに更更新をチェック ※ちなみにこのActivityは900⾏行行
ボタンをクリックしたらMessageをポストして結果を保存 @Override public void onClick(View v) { switch (v.getId()) {
case R.id.message_send_button: Message message = new Message(postMessageText); sendMessage(message); break; default: break; } } private void sendMessage(final Message message) { PostMessageRequest request = new PostMessageRequest(message, new Listener<JsonNode>() { @Override public void onResponse(JsonNode response) { JsonNode messageNode = response.get("message"); EightMessage message = new EightMessage(messageNode); mMessageDB.insert(message); mMessageList.add(message); // දࣔΛupdate } }); request.post(); } MessageActivity MessageDB mMessageDB; ArrayList<Message> mMessageList; -‐ void sendMessage(Message); ボタンをクリックしたら 通信して あれDBに保存しつつ 画⾯面に反映するActivity
MessageActivity MessageUsecase mMessageUsecase; MessageRepository mMessageRepository MessageRepository MessageDB mMessageDB;
ArrayList<Message> mMessageList; + void add(Message); + void setUpdateListener(Listener) MessageUsecase MessageRepository mMessageRepository; -‐ void sendMessage(Message); sendMessage() add() Listen 通信して結果を保存する 処理理をUsecaseとして分離離 DBに保存しつつ Listを管理理するクラスを Repositoryとして分離離 Repositoryからの updateの通知を受けてViewをupdate
Presentation Layer 7JFX Presenter Domain Layer 6TFDBTF Infrastructure Layer Entity
%# /FUXPSL Application Layer Activity Use-‐‑‒case部分を 切切り出せた! Activity
Activityから Presenterを切切り出す
7JFX Activity 6TFDBTF onClick等のイベント 表⽰示データのセット public class FeedFragment extends AbstractPagerFragment
{ private RecyclerView mFeedListView; private SwipeRefreshLayout mSwipeRefreshLayout; private TextView mTalkIconBadge; private TextView mNewsIconBadge; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_feed, container, false); mFeedListView = (RecyclerView) view.findViewById(R.id.fragment_feed_recycler_view); mTalkIconBadge = (TextView) view.findViewById(R.id.action_bar_badge_icon_count); mNewsIconBadge = (TextView) view.findViewById(R.id.action_bar_badge_icon_count); mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.fragment_feed_list_refresh); return view; } これまでの実装: Viewを制御するコードがすべてActivityに
MVVMパターン Data Binding from Android Studio 1.3
7JFX 7JFX.PEFM Activity 表⽰示データの取得 6TFDBTF onClick等のイベント public class CreatePostActivity extends
Activity { CreatePostViewModel mViewModel; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mViewModel = new CreatePostViewModel(); ActivityCreatePostBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_create_post); binding.setViewModel(mViewModel); }
Presentation Layer 7JFX ViewModel Domain Layer 6TFDBTF Infrastructure Layer Entity
%# /FUXPSL Application Layer Activity Presenter部分を ViewModelとして 切切り出す Use-‐‑‒case部分を 切切り出せた!
Presentation Layer 7JFX ViewModel Domain Layer 6TFDBTF Infrastructure Layer Entity
%# /FUXPSL Application Layer Activity Activityは ライフサイクル の管理理 画⾯面遷移の管理理 に集中すること ができる
そして、理理解しやすい メンテしやすいアプリケーションに
最後に
$PQZSJHIU4BOTBO *OD"MMSJHIUTSFTFSWFE 4BOTBOҰॹʹ৽͍͠ՁΛ࡞͍ͬͯ͘ ؒΛ͕͍ͯ͞͠·͢ɻ 3VCZ 3VCZPO3BJMT ʢ8FCΞϓϦέʔγϣϯʣ $ɼ"41/&5.7$ ʢ8FCΞϓϦέʔγϣϯʣ J04"OESPJEΞϓϦ
ݸਓ໊͚ཧΞϓϦʮ&JHIUʯ ໊σʔλԽࢄॲཧγεςϜ ๏ਓ໊͚ཧαʔϏεʮ4BOTBOʯ ๏ਓ໊͚ཧαʔϏε ʮ4BOTBOʯ ݸਓ໊͚ཧΞϓϦʮ&JHIUʯ ΤϯδχΞืूத 4BOTBO࠾༻ ݕࡧ SFDSVJU!TBOTBODPN·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ
ご清聴 ありがとうございました。