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.8k
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.7k
みんな大好き拡張関数 #kotlin_sansan
boohbah
2
9.3k
sealed class in Kotlin1.1
boohbah
1
1.8k
Sansanではたらくアプリエンジニアの20%ルール
boohbah
0
780
KotlinでDSL
boohbah
0
9.5k
ObservableArrayとPikkel
boohbah
2
1.4k
KotlinでPhantom Type #kotlin_sansan
boohbah
2
4k
#jkug Kotlinのclass delegation
boohbah
1
390
#DroidKaigi 既存のAndroidプロジェクトに Kotlinを導入した話
boohbah
5
2.7k
Other Decks in Programming
See All in Programming
KIKI_MBSD Cybersecurity Challenges 2025
ikema
0
1.3k
LLM Observabilityによる 対話型音声AIアプリケーションの安定運用
gekko0114
2
410
AI時代の認知負荷との向き合い方
optfit
0
130
CSC307 Lecture 03
javiergs
PRO
1
490
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
7
2.2k
Grafana:建立系統全知視角的捷徑
blueswen
0
320
AI Agent Tool のためのバックエンドアーキテクチャを考える #encraft
izumin5210
6
1.8k
余白を設計しフロントエンド開発を 加速させる
tsukuha
7
2.1k
15年続くIoTサービスのSREエンジニアが挑む分散トレーシング導入
melonps
0
140
それ、本当に安全? ファイルアップロードで見落としがちなセキュリティリスクと対策
penpeen
7
2.4k
今こそ知るべき耐量子計算機暗号(PQC)入門 / PQC: What You Need to Know Now
mackey0225
3
360
副作用をどこに置くか問題:オブジェクト指向で整理する設計判断ツリー
koxya
1
580
Featured
See All Featured
Building the Perfect Custom Keyboard
takai
2
680
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.2k
The SEO identity crisis: Don't let AI make you average
varn
0
62
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
2
400
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
A Tale of Four Properties
chriscoyier
162
24k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.2k
Imperfection Machines: The Place of Print at Facebook
scottboms
269
14k
Producing Creativity
orderedlist
PRO
348
40k
Optimizing for Happiness
mojombo
379
71k
Fireside Chat
paigeccino
41
3.8k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.3k
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·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ
ご清聴 ありがとうございました。