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.5k
みんな大好き拡張関数 #kotlin_sansan
boohbah
1
8.9k
sealed class in Kotlin1.1
boohbah
1
1.6k
Sansanではたらくアプリエンジニアの20%ルール
boohbah
0
710
KotlinでDSL
boohbah
0
8.9k
ObservableArrayとPikkel
boohbah
2
1.2k
KotlinでPhantom Type #kotlin_sansan
boohbah
2
3.8k
#jkug Kotlinのclass delegation
boohbah
1
330
#DroidKaigi 既存のAndroidプロジェクトに Kotlinを導入した話
boohbah
5
2.5k
Other Decks in Programming
See All in Programming
Scalaから始めるOpenFeature入門 / Scalaわいわい勉強会 #4
arthur1
1
340
KubeCon + CloudNativeCon NA 2024 Overviewat Kubernetes Meetup Tokyo #68 / amsy810_k8sjp68
masayaaoyama
0
260
Beyond ORM
77web
7
960
PHPUnitしか使ってこなかった 一般PHPerがPestに乗り換えた実録
mashirou1234
0
230
PHPで学ぶプログラミングの教訓 / Lessons in Programming Learned through PHP
nrslib
3
300
ドメインイベント増えすぎ問題
h0r15h0
2
360
Jakarta EE meets AI
ivargrimstad
0
260
これが俺の”自分戦略” プロセスを楽しんでいこう! - Developers CAREER Boost 2024
niftycorp
PRO
0
190
クリエイティブコーディングとRuby学習 / Creative Coding and Learning Ruby
chobishiba
0
3.9k
なまけものオバケたち -PHP 8.4 に入った新機能の紹介-
tanakahisateru
1
120
Webエンジニア主体のモバイルチームの 生産性を高く保つためにやったこと
igreenwood
0
340
StarlingMonkeyを触ってみた話 - 2024冬
syumai
3
270
Featured
See All Featured
Intergalactic Javascript Robots from Outer Space
tanoku
270
27k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
32
2.7k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
29
2k
Designing for humans not robots
tammielis
250
25k
Why You Should Never Use an ORM
jnunemaker
PRO
54
9.1k
StorybookのUI Testing Handbookを読んだ
zakiyama
27
5.3k
GraphQLとの向き合い方2022年版
quramy
44
13k
GitHub's CSS Performance
jonrohan
1030
460k
How to Think Like a Performance Engineer
csswizardry
22
1.2k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
5
450
YesSQL, Process and Tooling at Scale
rocio
169
14k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
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·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ
ご清聴 ありがとうございました。