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
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Jumpei Yamamoto
August 20, 2015
Programming
1.9k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Android開発における、レガシーコードと戦うためのリファクタリング
2015/8/20 マネーフォワード×Eight -モバイルアプリ勉強会
Android開発におけるリファクタリングの実際を紹介します。
Jumpei Yamamoto
August 20, 2015
More Decks by Jumpei Yamamoto
See All by Jumpei Yamamoto
KotlinでDSLを作る #Kotlin_Sansan
boohbah
2
1.8k
みんな大好き拡張関数 #kotlin_sansan
boohbah
2
9.6k
sealed class in Kotlin1.1
boohbah
1
1.8k
Sansanではたらくアプリエンジニアの20%ルール
boohbah
0
820
KotlinでDSL
boohbah
0
9.9k
ObservableArrayとPikkel
boohbah
2
1.4k
KotlinでPhantom Type #kotlin_sansan
boohbah
2
4k
#jkug Kotlinのclass delegation
boohbah
1
420
#DroidKaigi 既存のAndroidプロジェクトに Kotlinを導入した話
boohbah
5
2.8k
Other Decks in Programming
See All in Programming
レビュー履歴をAIに食わせて、 Compose移行を加速するs
shihochan
0
260
AI に Inclusive UI を書かせよう — Design Rules Skill で Compose UI を作り直す
theoriatec2024
1
420
Building an Out-of-Order CPU
latte72
0
580
自分的「カンファレンスの楽しみ方」
syumai
0
180
[DroidKaigi 26] How We Moved from Android Studio to Slack
thisaay
0
180
いまどきの Codex で開発する visionOS アプリの開発スタイルについて
karad
0
180
PyO3 で既存 Python 評価器を Rust core 化する ー wasm-bindgen でブラウザにも配るための設計
kdash
1
330
不幸な GC
chencmd
0
870
FastAPI の並行処理モデルを完全に理解する
hoto17296
9
3.7k
KotlinConf Extended South Korea 2026 Keynote
l2hyunwoo
0
130
MySQLとPostgreSQLって何が違うの?
akagami
PRO
0
160
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
200
Featured
See All Featured
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
230
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
Balancing Empowerment & Direction
lara
6
1.3k
ラッコキーワード サービス紹介資料
rakko
1
4.7M
How STYLIGHT went responsive
nonsquared
100
6.3k
First, design no harm
axbom
PRO
2
1.3k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.6k
Technical Leadership for Architectural Decision Making
baasie
3
550
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
460
How to Talk to Developers About Accessibility
jct
2
530
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.7k
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·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ
ご清聴 ありがとうございました。