【しくじり先生】 Unity(C#)からFlutterに転向してハマった落とし穴 ~Flutter開発で気にしなくていい3つのこと~

119 Views

September 29, 25

スライド概要

第8回 Flutter Gakkai で発表したスライドの共有です。
https://fluttergakkai.connpass.com/event/359514/

profile-image

Flutterエンジニア

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

【しくじり先生】 Unity(C#)からFlutterに転向してハマった落とし穴 ~Flutter開発で気にしなくていい3つのこと~ 株式会社SODA Flutterエンジニア TP

2.

はじめに 【概要】 他のプラットフォームからの転向だからこそ ハマったポイントをご紹介 【主な対象者】 - Flutterを始めた方(特に、転向した方) Flutterのエコシステムを支える概念に興味の ある方

3.

目次 ● ● ● 自己紹介 はじめに 気にしなくてよい3つのこと ○ ○ ○ ● ヒープメモリ 依存関係の逆転 アーキテクチャパターン まとめ

4.

~気にしなくていい3つのこと~ その1:ヒープメモリ

5.

ヒープメモリとは? ヒープ領域(英: heap area, heap memory)は コンピュータープログラミングにおいて、動的に確保可能なメモリの領域。 https://ja.wikipedia.org/wiki/%E3%83%92%E3%83%BC%E3%83%97%E9%A0%98%E5%9F%9F より引用

6.

ヒープメモリの関連ワード malloc ガベージコレクタ ポインタ アロケーション メモリリーク

7.

ガベージコレクタ ● 使わなくなったヒープ領域を自動で解放するための仕組み ● Dartのエンジンはこの仕組みを採用している(その他多くの言語も採用) ● 開発者をメモリ管理の苦しみから解放 👍

8.

Unityとガベージコレクタ ● Unity (C#) にもガベージコレクタが実装されている ● しかし、ゲーム開発という特性上、ヒープメモリを完全に忘れることができない ● 頻繁にヒープメモリ割り当て(アロケーション)を起こすとフレーム落ちが発生する ↓前職でこんな記事を書いたりしました Unityでゲームシーン実行中のGC Allocを ゼロに保つために気をつけていること (https://qiita.com/TP113/items/69858 24fee01bffd0526) 😇

9.

私の失敗(ヒープメモリ) ● Flutterを書く時にメモリアロケーションを気にしてしまった ● → メモリ節約のためmutableなクラスでプログラムしようとする ● → immutableオブジェクトのメリットを享受できず、コードが複雑になってしまった Listの大量生成は 避けたい… 全部Freezedクラス で本当に大丈夫…? 😭

10.

Flutterとヒープメモリ ● データを表現するクラスは基本的にimmutableで作る! ● ヒープメモリ割り当てを気にしなくてもパフォーマンスの問題になることはほぼない ○ Flutterにおいて、メモリの割り当て(状態更新)はユーザーの操作のタイミングに限られる →多くても1秒に数回 ○ アニメーションさせてないとフレーム落ちにそもそも気づかない 結論:ヒープメモリは気にしない

11.

~気にしなくていい3つのこと~ その2:依存関係の逆転

12.

依存関係の逆転とは? ● SOLID原則の5番目の法則(依存関係逆転の原則) ● 「上位レベル(抽象)の実装コードが下位レベル(詳細)に依存しないようにするべきである」 ● 制御の方向と依存の方向を独立させるためにインターフェースを活用する クラス A 上位 インター フェース B クラス B 下位

13.

C#とインターフェース ● C#はとにかくインターフェースを活用する言語 ● 明示的にインターフェースを定義しないとMockの作成や依存性の注入もままならない ○ ● 作っていないと後悔する場面が多い 結果、C#erはあらゆる境界にインターフェースを定義しがち public interface IMyRepository{ Result get(int id); Result post(int id, string name); } ← C#におけるinterface

14.

私の失敗(依存関係の逆転) ● Flutterでも明示的なインターフェースを作りまくった(特にRepositoryまわり) ● → Dartの仕様により、インターフェースは勝手に定義されている ● → 明示的にインターフェースを作る意味が薄い interface MyRepository{ Result get(int id); Result post(int id, string name); } class MyRepositoryImpl implements MyRepository{ MyAPI api; Result get(int id){ return api.get(id); } Result post(int id, string name){ return get.post(id, name) } } ← Flutterにおけるinterface (Dart3からキーワードが追加された)

15.

Flutterとインターフェース ● クラスを作る時に勝手に定義される(Dartの仕様) ○ 明示的インターフェース(Java, C#)とダックタイプ(Go, Python)の中間みたいな言語仕 様? ○ 暗黙的なインターフェース定義と、明示的な実装 ● 明示的なインターフェースがなくてもMockを作れる ● その他、インターフェースで実装を分岐させたいケースもあまりない ○ あったとしても後から分けるのが容易 ○ 凝ったSDKを作る時は使うかも ○ アプリを作るだけならほぼ使わない 結論:依存性の逆転(および明示的インターフェース定義)は気にしない

16.

~気にしなくていい3つのこと~ その3:アーキテクチャパターン

17.

クライアントサイドにおけるアーキテクチャパターン ● 状態をいかに画面に反映するか、ということに注目したものが多い ● MVC, MVP, MVVM など ● Model ↔ View をどう繋ぐか View Model Controller ViewModel View Model

18.

私の失敗(アーキテクチャパターン) ● RiverpodでMVC, MVVMを実装しようとしてしまった ● → それっぽい構造は作れるけど、なんだか無駄が多い… ● → Riverpodのベストプラクティスから離れてしまう… 🤔

19.

Riverpodとアーキテクチャパターン ● Riverpodのベストプラクティスがそのままアーキテクチャパターンになっている ● 下手に他のアーキテクチャパターンを混ぜない方がいい ● SSoTとCQS (コマンドクエリ分離) に気をつけてProviderを実装すればほとんど問題ない 👇まずはこの2つに従えば十分 - Riverpod公式ドキュメント「Do/Don't」 (https://riverpod.dev/ja/docs/essentials/do_dont) Flutterアプリにおける、過不足ない設計の考察 by mono (https://medium.com/flutter-jp/architecture-240d3c56b597) 結論:アーキテクチャパターンは気にしない

20.

まとめ ● Flutter+Riverpodのエコシステムが優れているので、それらのベストプラクティスに素直に従えば ほとんどのアプリは問題なく開発できる ● アンラーニングの大切さを実感した ○ ● 郷に入っては郷に従え プラットフォームごとのお作法の違いを学ぶのは楽しい