Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ぼくのかんがえた最強のUsecaseの作り方~あるいはビジネスロジックとはなにかという1つの回答~

 ぼくのかんがえた最強のUsecaseの作り方~あるいはビジネスロジックとはなにかという1つの回答~

ビジネスロジック、Domain層、UseCase。これらはMVVMやMVP、CleanArcitectureなど、3-Layeredアーキテクチャーではよく聞く単語だ。

しかし、いざ設計しようとすると
「つまり、ビジネスロジック/Domain/UseCaseって何?」
「レポジトリから取得したデータをそのまま渡すだけになるんだけど、これでいいの?」

などなど、首をひねりながら設計する人は多いのではないだろうか。かくいう私もその一人だった。

今回の発表では、CleanArchitectureにフォーカスし、「ビジネスロジック/Domain/UseCase is 何」という疑問を紐解いていこうと思う。
具体的には個人で開発しているアプリの「らくでん」の実装を元に、
・ビジネスロジックってどういうものを指すのか
・Domain層(UseCase)をどのように設計していくか
・何をDomain層に入れるか/入れないか
を話していく予定だ。

ReferenceURL:
https://github.com/kiuchikeisuke/RakutenCall

Keisuke kiuchi

February 07, 2019
Tweet

More Decks by Keisuke kiuchi

Other Decks in Programming

Transcript

  1. ΅͘ͷ͔Μ͕͑ͨ࠷ڧͷ6TF$BTFͷ࡞Γํ

    d͋Δ͍͸ϏδωεϩδοΫͱ͸Կ͔ͱ͍͏̍ͭͷճ౴d
    ,FJTVLF,[email protected]
    d3PPN
    IUUQTHJUIVCDPNLJVDIJLFJTVLF3BLVUFO$BMM

    View Slide

  2. ໦಺ܒี
    w ৬ۀɿ:ͷ"OESPJEΤϯδχΞ݉ҭࣇύύ
    w झຯͰ࡞ͬͨϞϊɿʮΒ͘ͰΜ "QQ
    ʯɺ

    ʮ$MFBO"SDIJUFDUVSF5FNQMBUF'PS,PUMJO 044
    ʯ
    w Ϟοτʔɿ໌೔ͷࣗ෼ʹ༏͍͠ίʔυΛॻ͘
    w 4/4

    [email protected]
    (JUIVC!LJVDIJLFJTVLF

    [email protected]

    View Slide

  3. ࠷ۙͷΞΫςΟϏςΟ

    վળ͓͡͞Μܑ͓͞Μ
    ύού#
    ΢σϚΤ9੒ΓཱͯͷΠΧΨʔϧ

    View Slide

  4. ஫ҙࣄ߲
    w ͜ͷൃදͰ͸$MFBO"SDJUFDUVSFͱ͸ͳΜͧ΍ʁతͳ࿩͸͋·Γ͠ͳ͍Ͱ͢
    w ͜ͷൃද͸ϢʔεέʔεϏδωεϩδοΫͱ͸͔͋͘Δ΂͠ͱ

    ڧཁ͢Δ΋ͷͰ͸͋Γ·ͤΜ͠ɺઈରతͳਖ਼ղͰ΋͋Γ·ͤΜ
    w ͜ͷൃදΛฉ͘ͱૄ݁߹ͰՄಡੑͷߴ͍ίʔυ͕ॻ͚ΔΑ͏ʹͳΔ

    ͔΋͠Ε·ͤΜ
    w ͜ͷൃදͰ͸ཧղଅਐͷͨΊօ͞ΜʹखΛ্͛ͯ΋Β͏γʔϯ͕Կ౓͔͋Γ
    ·͢ɻͰ͖Ε͹ੵۃతʹࢀՃΛ͓ئ͍͠·͢

    View Slide

  5. ϏδωεϩδοΫϢʔεέʔεͬͯԿʁ
    d͋Δ͍͸ͨͩͷ໰୊ఏىd

    View Slide

  6. ϏδωϧϩδοΫϢʔεέʔε
    ͱ͸Կ͔Λҙࣝͯ͠࢖͍ͬͯΔਓ

    View Slide

  7. ϏδωεϩδοΫϢʔεέʔεͱ͸Կ͔Λ

    ʮΒ͘ͰΜʯΞϓϦΛྫʹߟ͍͑ͯ͜͏

    View Slide

  8. ʮΒ͘ͰΜʯΞϓϦͱ͸ʁ
    w ָఱͰΜΘαʔϏεͷαϙʔτΞϓϦʢࣗ࡞ʣ
    w ָఱͰΜΘαʔϏεͱ͸ɺి࿩͢Δࡍʹ

    ಛఆͷ1SFpYΛ಄ʹ͚ͭΔ͜ͱͰ

    ͋ΒΏΔి࿩͕෼·Ͱ͸ेԁʹͳΔαʔϏε ౰࣌

    w Β͘ͰΜ͸ීஈͷి࿩$BMMΠϕϯτʹϑοΫͯ͠

    ࣗಈͰ1SFpYΛ͚ͭΔΞϓϦ
    w

    View Slide

  9. γϛϡϨʔτֶͯ͠Ϳ

    ϏδωεϩδοΫͱϢʔεέʔε
    d΋͋͠ͳ͕ͨΒ͘ͰΜͷ։ൃऀʹͳͬͨΒʁd

    View Slide

  10. ͔͜͜Β͸ɺ΋͋͠ͳ͕ͨ

    Β͘ͰΜͷ։ൃऀʹ೚໋͞ΕͨΒʁ

    ͱ͍͏γνϡΤʔγϣϯͰ࿩͕ਐΈ·͢'

    View Slide

  11. Β͘ͰΜΞϓϦͷ։ൃऀʹ೚໋͞ΕͨΒ·ͣԿΛ͢Δʁ
    ·ͣ͸ίʔυΛॻ͖࢝ΊΔ✍
    ·ͣ͸σβΠϯνʔϜʹσβΠϯΛฉ͖ʹߦ͘
    ·ͣ͸ϏδωενʔϜ ࢓༷ݕ౼νʔϜ
    ʹ࢓༷ͷৄࡉΛ
    ฉ͖ʹߦ͘

    View Slide

  12. Β͘ͰΜΞϓϦͷ։ൃऀʹ೚໋͞ΕͨΒ·ͣԿΛ͢Δʁ
    ·ͣ͸ίʔυΛॻ͖࢝ΊΔ✍
    ·ͣ͸σβΠϯνʔϜʹσβΠϯΛฉ͖ʹߦ͘
    ·ͣ͸ϏδωενʔϜ ࢓༷ݕ౼νʔϜ
    ʹ࢓༷ͷৄࡉΛ
    ฉ͖ʹߦ͘
    ଟ෼ΈΜͳ͜Ε͔Β΍Γ·͢ΑͶ

    View Slide

  13. ԿΛϏδωενʔϜʹ

    ࣭໰͠Α͏

    View Slide

  14. ϏδωενʔϜ΁ͷ࣭໰ྫ
    1SFpYͷ൪߸͸ʁ

    6*σβΠϯ͸͍ͭࠒͰ͖Δʁ

    αʔόʔ࿈ܞͱ͔͸͋Δʁͳ͍ʁ

    ͱ͔ͱ͔ຊདྷి࿩ྉ͕ۚ

    ͔͔Βͳ͍ి࿩൪߸ͷѻ͍͸ʁ
    ࣭໰υ΢κ

    View Slide

  15. ϏδωενʔϜͷճ౴ྫ
    1SFpYͷ൪߸͸ʁ

    ˠ๭3͔ࣾΒ࿈བྷ଴ͪͰ͢
    6*σβΠϯ͸͍ͭࠒͰ͖Δʁ

    ˠ6*νʔϜʹฉ͍ͯΈͳ͍ͱԿͱ΋
    αʔόʔ࿈ܞͱ͔͸͋Δʁͳ͍ʁ

    ˠࠓ࣌఺Ͱ͸Կͱ΋
    ͱ͔ͱ͔ຊདྷి࿩ྉ͕ۚ

    ͔͔Βͳ͍ి࿩൪߸ͷѻ͍͸ʁˠݕ౼͠·͢ʂʂ
    ճ౴σε

    View Slide

  16. ྉۚͷ͔͔Βͳ͍ి࿩൪߸ͬͯ
    Կ͕͋ΔͩΖ͏

    View Slide

  17. ྉۚͷ͔͔Βͳ͍ి࿩൪߸
    w ౳ͷٹٸͷి࿩൪߸

    w ౳ͷϑϦʔμΠΞϧͷి࿩൪߸

    w Ո଒ׂԁ౳Ͱࢦఆ͞Εͨɺಛผͳి࿩൪߸
    ߟ͑த

    View Slide

  18. ͦΜͳંɺϏδωενʔϜ͔Β
    ࢓༷͕ग़͖ͯ·ͨ͠

    View Slide

  19. جຊํ਑͸ ಛఆͷ৚݅ʹ֘౰͢Δి࿩
    ൪߸ͳΒ1SFpY͚ͭͳ͍Ͱ΄͍͠Ͱ͢
    ίϨσ

    Ϥϩd

    View Slide

  20. ఏࣔ͞Εͨৄࡉͳ࢓༷
    w ౳ͷٹٸͷి࿩൪߸

    ˠઃఆ͕༗ޮͳΒ1SFpYΛ෇͚ͳ͍Α͏ʹͯ͠΄͍͠
    w ౳ͷϑϦʔμΠΞϧͷి࿩൪߸

    ˠઃఆ͕༗ޮͳΒ1SFpYΛ෇͚ͳ͍Α͏ʹͯ͠΄͍͠
    w Ո଒ׂԁ౳Ͱࢦఆ͞Εͨɺಛผͳి࿩൪߸

    ˠϢʔβ͕1SFpYΛ෇͚ͳ͍ѼઌΛબ୒Ͱ͖Δ ແࢹϦετ
    Α͏ʹͯ͠ɺ

    ɹ֘౰ͷѼઌͳΒ1SFpYΛ෇͚ͳ͍Α͏ʹͯ͠΄͍͠
    LXTL

    View Slide

  21. ͸͍ɺ͜ΕΒ͕ϏδωεϩδοΫͱ
    ϢʔεέʔεͰ͢

    View Slide

  22. View Slide

  23. ϏδωεϩδοΫͱ͸
    dϏδωενʔϜ͕஌ͬͯΔ΂͖ϩδοΫ ࢓༷
    d

    View Slide

  24. ઌ΄ͲͷγϛϡϨʔτͰɺ
    ϏδωενʔϜ͚ͩͰܾఆͰ͖ͨ͜ͱ͸
    Կ͔
    ࢥ͍ͩͨ͠

    View Slide

  25. ϏδωενʔϜͷճ౴
    1SFpYͷ൪߸͸ʁ

    ˠ๭3͔ࣾΒ࿈བྷ଴ͪͰ͢
    6*σβΠϯ͸͍ͭࠒͰ͖Δʁ

    ˠ6*νʔϜʹฉ͍ͯΈͳ͍ͱԿͱ΋
    αʔόʔ࿈ܞͱ͔͸͋Δʁͳ͍ʁ

    ˠࠓ࣌఺Ͱ͸Կͱ΋
    ͱ͔ͱ͔ຊདྷి࿩ྉ͕ۚ

    ͔͔Βͳ͍ి࿩൪߸ͷѻ͍͸ʁ

    ˠ1SFpYΛ͚ͭͳ͍ํ਑Ͱ
    ճ૝த

    View Slide

  26. ϏδωενʔϜͷճ౴
    1SFpYͷ൪߸͸ʁ

    ˠ๭3͔ࣾΒ࿈བྷ଴ͪͰ͢
    6*σβΠϯ͸͍ͭࠒͰ͖Δʁ

    ˠ6*νʔϜʹฉ͍ͯΈͳ͍ͱԿͱ΋
    αʔόʔ࿈ܞͱ͔͸͋Δʁͳ͍ʁ

    ˠࠓ࣌఺Ͱ͸Կͱ΋
    ͱ͔ͱ͔ຊདྷి࿩ྉ͕ۚ

    ͔͔Βͳ͍ి࿩൪߸ͷѻ͍͸ʁ

    ˠ1SFpYΛ͚ͭͳ͍ํ਑Ͱ
    3͕ࣾ౴͑Λ࣋ͬͯΔ
    σβΠϯνʔϜ͕౴͑
    Λ࣋ͬͯΔ
    ະདྷͷࢲ͕ͨͪ౴͑Λ
    ͍࣋ͬͯΔ
    ϏδωενʔϜ͕౴͑
    Λ͍࣋ͬͯͨ
    ճ૝த

    View Slide

  27. ϏδωεϩδοΫͱ͸
    ಛఆͷి࿩൪߸Ͱ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒ౳ͷٹٸͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒ౳ͷϑϦʔμΠϠϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wՈ଒ׂ౳ͷϢʔβ͕ࢦఆͨ͠Ѽઌʹ͸1SFpYΛ͚ͭͳ͍
    w ϏδωενʔϜ ࢓༷νʔϜ
    ͕஌͍ͬͯΔ΂͖࢓༷ ϩδοΫ
    ͷࣄ
    w ϏδωενʔϜ͚ͩͰܾ·Βͳ͍ࣄ 6*ͷσβΠϯ σʔλͷѻ͍ 1SFpYͷ
    ൪߸ͳͲ
    ͸ϏδωεϩδοΫʹ͸ؚΊͳ͍
    6*σβΠϯ
    1SFpYͷ൪߸
    σʔλอଘͷ࢓༷

    View Slide

  28. ϏδωεϩδοΫͱ͸
    ಛఆͷి࿩൪߸Ͱ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒ౳ͷٹٸͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒ౳ͷϑϦʔμΠϠϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wՈ଒ׂ౳ͷϢʔβ͕ࢦఆͨ͠Ѽઌʹ͸1SFpYΛ͚ͭͳ͍
    w ϏδωενʔϜ ࢓༷νʔϜ
    ͕஌͍ͬͯΔ΂͖࢓༷ ϩδοΫ
    ͷࣄ
    w ϏδωενʔϜ͚ͩͰܾ·Βͳ͍ࣄ 6*ͷσβΠϯ σʔλͷѻ͍ 1SFpYͷ
    ൪߸ͳͲ
    ͸ϏδωεϩδοΫʹ͸ؚΊͳ͍
    6*σβΠϯ
    1SFpYͷ൪߸
    σʔλอଘͷ࢓༷

    View Slide

  29. ϏδωεϩδοΫͱ͸
    w ϏδωενʔϜ ࢓༷νʔϜ
    ͕஌͍ͬͯΔ΂͖࢓༷ ϩδοΫ
    ͷࣄ
    w ϏδωενʔϜ͚ͩͰܾ·Βͳ͍ࣄ 6*ͷσβΠϯ σʔλͷѻ͍ 1SFpYͷ
    ൪߸ͳͲ
    ͸ϏδωεϩδοΫʹ͸ؚΊͳ͍
    ಛఆͷి࿩൪߸Ͱ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒٹٸͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒϑϦʔμΠϠϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wແࢹϦετͷѼઌʹ͸1SFpYΛ͚ͭͳ͍
    6*σβΠϯ
    1SFpYͷ൪߸
    σʔλอଘͷ࢓༷
    ٹٸͷి࿩
    ൪߸
    ϑϦʔμΠΞϧͷ൪߸
    Ϣʔβ͕ࢦఆ
    ͨ͠ແࢹϦετ
    ઃఆͷ༗ޮͷঢ়ଶ

    View Slide

  30. ͡Ό͋ϢʔεέʔεͬͯԿʁ

    ϏδωεϩδοΫͷ͜ͱʁ

    View Slide

  31. Ϣʔεέʔεͱ͸
    dϏδωεϩδοΫΛ࢖ΘΕΔέʔε͝ͱʹ෼ׂͨ͠΍ͭd

    View Slide

  32. ϏδωεϩδοΫ
    ಛఆͷి࿩൪߸Ͱ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒٹٸͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒϑϦʔμΠϠϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wແࢹϦετͷѼઌʹ͸1SFpYΛ͚ͭͳ͍

    View Slide

  33. ϏδωεϩδοΫ
    ಛఆͷి࿩൪߸Ͱ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒٹٸͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒϑϦʔμΠϠϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wແࢹϦετͷѼઌʹ͸1SFpYΛ͚ͭͳ͍
    ͜ΕΒ͕Ϣʔεέʔε

    View Slide

  34. Ϣʔεέʔεͱ͸
    w ϏδωεϩδοΫΛ࢖͏ Ϣʔε
    έʔε͝ͱʹ෼ׂͨ͠΋ͷ
    w Ϣʔεέʔεͷू߹ମ͕ϏδωεϩδοΫʹͳΔ
    ಛఆͷి࿩൪߸Ͱ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒٹٸͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wઃఆ͕༗ޮͳΒϑϦʔμΠϠϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍
    wແࢹϦετͷѼઌʹ͸1SFpYΛ͚ͭͳ͍

    View Slide

  35. ϏδωεϩδοΫϢʔεέʔεͷҰݴ·ͱΊ
    w ϏδωεϩδοΫͱ͸ɺ

    ϏδωενʔϜ ࢓༷νʔϜ
    ͕஌͍ͬͯΔ΂͖࢓༷ ϩδοΫ
    ͷࣄ
    w Ϣʔεέʔεͱ͸ɺ

    ϏδωεϩδοΫΛ࢖͏ Ϣʔε
    έʔε͝ͱʹ෼ׂͨ͠΋ͷ

    View Slide

  36. ͰɺϢʔεέʔεͱ͔

    ϏδωεϩδοΫ͕ղͬͯ

    Կ͕خ͍͠ͷʁ

    View Slide

  37. ϏδωεϩδοΫϢʔεέʔεͷϝϦοτ
    dίʔυ͕࢓༷ॻd

    View Slide

  38. ઃఆ͕༗ޮͳΒϑϦʔμΠΞϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍

    ͷ࣮૷
    import io.reactivex.Observable
    import jp.rakutencall.data.datasource.contacts.ContactsDataSource
    import jp.rakutencall.data.datasource.settings.SettingDataSource
    import jp.rakutencall.data.entity.number.PhoneNumber
    import jp.rakutencall.data.entity.prefix.Prefix
    import jp.rakutencall.utils.commons.ExecutionThreads
    import jp.rakutencall.utils.commons.IoUseCase
    import jp.rakutencall.utils.commons.UseCase
    import javax.inject.Inject
    class FreeDial @Inject constructor(
    private val settingDataSource: SettingDataSource,
    private val contactsDataSource: ContactsDataSource,
    executionThreads: ExecutionThreads)
    : IoUseCaseThrowable>(executionThreads) {
    override fun execute(requestValue: Request):
    Observable {
    return settingDataSource.getFreeDialEnable()
    .flatMap {
    if (it)
    validateFreeDial(requestValue)
    else
    Observable.just(Response(requestValue.prefix))
    }
    }
    private fun validateFreeDial(requestValue: Request):
    Observable {
    return contactsDataSource.getFreeDialPrefix().map {
    if (it.any { it.startWithPrefix(requestValue.phoneNumber) })
    Response(Prefix.generateEmptyPrefix())
    } else {
    Response(requestValue.prefix)
    }
    }.defaultIfEmpty(Response(requestValue.prefix))
    }
    data class Request(val prefix: Prefix, val phoneNumber: PhoneNumber)
    UseCase.RequestValue
    data class Response(val prefix: Prefix) : UseCase.ResponseValue
    }

    View Slide

  39. "OESPJEʹ΋42-JUFʹ΋ґଘͯ͠ͳ͍ૄ݁߹ͳΫϥε
    import io.reactivex.Observable
    import jp.rakutencall.data.datasource.contacts.ContactsDataSource
    import jp.rakutencall.data.datasource.settings.SettingDataSource
    import jp.rakutencall.data.entity.number.PhoneNumber
    import jp.rakutencall.data.entity.prefix.Prefix
    import jp.rakutencall.utils.commons.ExecutionThreads
    import jp.rakutencall.utils.commons.IoUseCase
    import jp.rakutencall.utils.commons.UseCase
    import javax.inject.Inject

    View Slide

  40. ࢓༷ͦͷ··ͳ࣮૷ ઃఆ͕༗ޮͳΒd

    class FreeDial @Inject constructor(
    private val settingDataSource: SettingDataSource,
    private val contactsDataSource: ContactsDataSource,
    executionThreads: ExecutionThreads)
    : IoUseCase(executionThreads) {
    override fun execute(requestValue: Request): Observable {
    return settingDataSource.getFreeDialEnable()
    .flatMap {
    if (it)
    validateFreeDial(requestValue)
    else
    Observable.just(Response(requestValue.prefix))
    }
    }

    View Slide

  41. ࢓༷ͦͷ··ͳ࣮૷ ϑϦʔμΠΞϧͷ൪߸ʹ͸1SFpYΛ͚ͭͳ͍

    private fun validateFreeDial(requestValue: Request): Observable {
    return contactsDataSource.getFreeDialPrefix().map {
    if (it.any { it.startWithPrefix(requestValue.phoneNumber) }) {
    Response(Prefix.generateEmptyPrefix())
    } else {
    Response(requestValue.prefix)
    }
    }.defaultIfEmpty(Response(requestValue.prefix))
    }
    data class Request(val prefix: Prefix, val phoneNumber: PhoneNumber) :
    UseCase.RequestValue
    data class Response(val prefix: Prefix) : UseCase.ResponseValue
    }

    View Slide

  42. ϏδωεϩδοΫϢʔεέʔεͷϝϦοτ
    w ૄ݁߹ͳΫϥε 3Y+BWBͱ%BHHFSʹ͔͠ґଘͯ͠ͳ͍

    ˠJ04΍αʔόʔ΁ͷҠ২͕༰қʹͰ͖Δ

    ˠ࢓༷ʹ௚݁͢ΔϏδωεϩδοΫΛ؆୯ʹςετͰ୲อͰ͖Δ
    w ࢓༷ͦͷ··͕࣮૷ʹͳ͍ͬͯΔ

    ˠՄಡੑ͕ΊͪΌͪ͘Όߴ͍ίʔυʹͳΔ

    ˠJ04ΤϯδχΞ΍αʔόʔνʔϜͱιʔεΛݩʹͯٞ͠࿦Ͱ͖Δ

    ͋ΘΑ͘͹ϏδωενʔϜͱ΋

    View Slide

  43. Ұݴ·ͱΊ
    d΅͘ͷ͔Μ͕͑ͨ࠷ڧͷ6TF$BTFͷ࡞Γํͱ͔৭ʑd
    ࢓༷͸
    ϏδωεϩδοΫ͸

    View Slide

  44. Ұݴ·ͱΊ
    w ϏδωεϩδοΫͱ͸ɺ

    ϏδωενʔϜ ࢓༷νʔϜ
    ͕஌͍ͬͯΔ΂͖࢓༷ ϩδοΫ
    ͷࣄ
    w Ϣʔεέʔεͱ͸ɺ

    ϏδωεϩδοΫΛ࢖͏ Ϣʔε
    έʔε͝ͱʹ෼ׂͨ͠΋ͷ
    w ͍͖͞ΐʔͳϢʔεέʔεͷ࡞Γํͱ͸ɺ

    ϏδωενʔϜ͚ͩͷձ࿩ͰܾఆͰ͖ͨ࢓༷͕Ϣʔεέʔε
    w ͍͖͞ΐʔͳϝϦοτͱ͸ɺ

    ΊͬͪΌૄ݁߹ͰՄಡੑͷߴ͍ίʔυ͕Ͱ͖Δ

    View Slide

  45. ͓ΘΓ

    View Slide