コードの互換性と進化の両立

10,052 views

Published on

Jenkinsでつちかった、コードの互換性を保ちつつ様々な修正を加えていく技法を紹介します。

Published in: Technology
0 Comments
24 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
10,052
On SlideShare
0
From Embeds
0
Number of Embeds
1,103
Actions
Shares
0
Downloads
36
Comments
0
Likes
24
Embeds 0
No embeds

No notes for slide
  • 本当に実行するまでわからない
  • Jenkinsでも似たような問題に何度も遭遇した
  • でも進化しないといけない、どうするか。
  • コードの進化と互換性の狭間の苦しみからどう解脱するか。徐々に蓄積してきたノウハウ・ツールを大公開
  • コンパイルはできないが動く。ソースコード上の規則とバイナリ上の規則の違い。
  • プログラムを書き換えればいいじゃんか
  • コードの互換性と進化の両立

    1. 1. 互換性とコード進化の両立 川口耕介 CloudBees, Inc. kk@kohsuke.org / @kohsukekawa ©2013 CloudBees, Inc. All Rights Reserved 1
    2. 2. 分散・独立・並行開発 ©2013 CloudBees, Inc. All Rights Reserved 2
    3. 3. http://commons.wikimedia.org/wiki/File:Close_up_of_Hand_Cut_Jigsaw_Puzzle.JPG ©2013 CloudBees, Inc. All Rights Reserved 3
    4. 4. 実行時結合 ©2013 CloudBees, Inc. All Rights Reserved 4
    5. 5. 共通ライブラリ ウェブアプリX モジュールA モジュールB ライブラリ v1.0 ライブラリ v2.0 ©2013 CloudBees, Inc. All Rights Reserved 5
    6. 6. ©2013 CloudBees, Inc. All Rights Reserved 6
    7. 7. ©2013 CloudBees, Inc. All Rights Reserved © いまいまさのぶ 7
    8. 8. プラグイン機構 プラグイン プラグイン コア プラグイン プラグイン 様々なクラス ©2013 CloudBees, Inc. All Rights Reserved … 80+のライブラリ 8
    9. 9. 拡張ポイント • コア interface Animal { void bark(); } • プラグイン @Extension class Dog implements Animal { void bark() { print(“ワン!”); } } ©2013 CloudBees, Inc. All Rights Reserved 9
    10. 10. プラグインはコアをガンガン使う Project p = …; Future<Build> f = p.scheduleBuild(); Build b = f.get(); ©2013 CloudBees, Inc. All Rights Reserved 10
    11. 11. この苦しみから解脱する方法はないかと • お釈迦様の絵 • 徐々に蓄積してきたノウハウ・ツールを大公 開 ©2013 CloudBees, Inc. All Rights Reserved 11
    12. 12. ©2013 CloudBees, Inc. All Rights Reserved mage © http://sfcitizen.com/blog/wp-content/uploads/2011/11/6302790910_c4eb865892_o-copy.jpg 12
    13. 13. フィールドの隠蔽 class Point { int x,y; } class Point { int getX(); int getY(); void setX(int); void setY(int); } ©2013 CloudBees, Inc. All Rights Reserved 13
    14. 14. インターフェース?抽象クラス? interface Animal { void bark(); } abstract class Animal { public abstract void bark(); } ©2013 CloudBees, Inc. All Rights Reserved 14
    15. 15. インターフェース?抽象クラス? interface Animal { void bark(); void bark(int times); } ©2013 CloudBees, Inc. All Rights Reserved 15
    16. 16. インターフェース?抽象クラス? abstract class Animal { public abstract void bark(); public void bark(int times) { for (int i=0; i<times; i++) bark(); } } ©2013 CloudBees, Inc. All Rights Reserved 16
    17. 17. ピンポンパターン abstract class Animal { @deprecated public void bark() { bark(1); } public void bark(int times) { for (int i=0; i<times; i++) bark(); } } ©2013 CloudBees, Inc. All Rights Reserved 17
    18. 18. コンストラクタの肥大化 • 外部コードによる サブクラス化を許可 したい • GitSCMの例 public GitSCM( String scmName, List<UserRemoteConfig> userRemoteConfigs, List<BranchSpec> branches, UserMergeOptions userMergeOptions, Boolean doGenerateSubmoduleConfigurations, Collection<SubmoduleConfig> submoduleCfg, boolean clean, boolean wipeOutWorkspace, BuildChooser buildChooser, GitRepositoryBrowser browser, String gitTool, boolean authorOrCommitter, String relativeTargetDir, String reference, String excludedRegions, String excludedUsers, String localBranch, boolean disableSubmodules, boolean recursiveSubmodules, boolean pruneBranches, boolean remotePoll, String gitConfigName, String gitConfigEmail, boolean skipTag, String includedRegions, boolean ignoreNotifyCommit, boolean useShallowClone) { ©2013 CloudBees, Inc. All Rights Reserved 18
    19. 19. 我慢してセッター class Foo { void initXYZ(int x, int y, int z) { … } void initABC(String a, String b, String c) { … } } ©2013 CloudBees, Inc. All Rights Reserved 19
    20. 20. コンフィグパターン class FooConfig { int x,y,z; String a,b,c; } abstract class Foo { Foo(FooConfig config) { … } } ©2013 CloudBees, Inc. All Rights Reserved 20
    21. 21. ©2013 CloudBees, Inc. All Rights Reserved mage © http://sfcitizen.com/blog/wp-content/uploads/2011/11/6302790910_c4eb865892_o-copy.jpg 21
    22. 22. パッケージ越しにアクセスするた めにpublicなんだけど、 外部からは使わせたくない ©2013 CloudBees, Inc. All Rights Reserved 22
    23. 23. 互換性用に@deprecatedとした コードを次のステップに進めたい ©2013 CloudBees, Inc. All Rights Reserved 23
    24. 24. だ をつ 修 ア 俺 し く 飾ク俺 て る 子セ ! 道 をス 具 © 小学館 ©2013 CloudBees, Inc. All Rights Reserved 24
    25. 25. http://kohsuke.org/access-modifier/ • 独自のアクセスチェッカを定義する class 俺俺アクセス extends AccessRestriction { public void instantiated(loc,target,listener) { listener.onError(null,loc, target+“インスタンス生成は禁止”); } } • サブタイプ、読み、書き、呼び出しなど6種 類 ©2013 CloudBees, Inc. All Rights Reserved 25
    26. 26. http://kohsuke.org/access-modifier/ • 通常のアクセス修飾子と共に使う @Restricted(俺俺アクセス.class) public class Foo { public String value; } • Mavenプラグインがクラスファイルを検査 ©2013 CloudBees, Inc. All Rights Reserved 26
    27. 27. ©2013 CloudBees, Inc. All Rights Reserved 27
    28. 28. バイナリ互換性を活用 interface Animal { void bark(); } class Dog implements Animal { void bark() { print(“ワン”); } } Animal a = new Dog(); a.bark(); ©2013 CloudBees, Inc. All Rights Reserved 28
    29. 29. バイナリ互換性を活用 interface Animal { void bark(int n); } 1. new Dog()でInstantiationError class Dog implements Animal{ 2. Dog→Animalのキャストで ClassCastException void bark() { print(“ワン”); } 3. a.bark(3)でAbstractMethodError } 4. その他 Animal a = new Dog(); a.bark(3); ©2013 CloudBees, Inc. All Rights Reserved 29
    30. 30. try { a.bark(3); } catch (AbstractMethodError e) { // 互換性モード … } ©2013 CloudBees, Inc. All Rights Reserved 30
    31. 31. ジェネリクス class Foo { List<Object> getChildren() { … } } class Foo { List<String> getChildren() { … } } ©2013 CloudBees, Inc. All Rights Reserved 31
    32. 32. ジェネリクス class Foo<T extends X> implements Future<T> { T get() { … } } class Foo<T extends Y> implements Future<T> { T get() { … } } class Y extends X { … } ©2013 CloudBees, Inc. All Rights Reserved 32
    33. 33. ジェネリクス型変更のルール • メソッド・フィールドのErasureが変わって いなければOK – T<X,Y,…> → T – T extends X → X –… • 検査してくれるツールをいつかは書きたい ©2013 CloudBees, Inc. All Rights Reserved 33
    34. 34. 根底のルール foo/Foo.class invokevirtual org.example.Bar#method1(java.lang.String,int,int)boolean … org/example/Bar.class メソッド method1 (java.lang.String,int,int):boolean ©2013 CloudBees, Inc. All Rights Reserved 34
    35. 35. 参照解決のルール • メソッドへの参照 – クラス名: – メソッド名: – 戻り値型: – パラメータ型: java.lang.String indexOf int java.lang.String,int • 関連事項 – アクセス修飾子 – 例外 ©2013 CloudBees, Inc. All Rights Reserved 35
    36. 36. コード進化のパターン class Foo { X get() { … } } class Foo { Y get() { … } } class Y extends X { … } ©2013 CloudBees, Inc. All Rights Reserved 36
    37. 37. コード進化のパターン class Foo { X get() { … } } class Foo { Y get() { … } X get() { … } } ©2013 CloudBees, Inc. All Rights Reserved 37
    38. 38. Bridge Method Injectorプロジェクト bit.ly/b-mi class Foo { @WithBridgeMethods(X.class) Y get() { … } } class Foo { Y get() { … } X get() { Y y=get(); return y; } } ©2013 CloudBees, Inc. All Rights Reserved 38
    39. 39. 応用 class Foo { @WithBridgeMethods(value=X.class, castRequired=true) Object get() { … } } class Foo { Object get() { … } X get() { Object o=get(); return (X)o; } } ©2013 CloudBees, Inc. All Rights Reserved 39
    40. 40. 自分はルールを守っていても モジュールB ライブラリ 1.0 モジュールA ライブラリ 2.0 ©2013 CloudBees, Inc. All Rights Reserved 40
    41. 41. シェーディング / パッケージ・リネーミング package org.jenkinsci.foo; import org.acme.A; import org.acme.B; class Foo { private List<A> a = …; void bar() { new B().b(); } } ©2013 CloudBees, Inc. All Rights Reserved 41
    42. 42. シェーディング / パッケージ・リネーミング package org.jenkinsci.foo; import hidden.org.acme.A; import hidden.org.acme.B; class Foo { private List<A> a = …; void bar() { new B().b(); } } ©2013 CloudBees, Inc. All Rights Reserved 42
    43. 43. シェーディング / パッケージ・リネーミング • 事後に – maven-shade-plugin • 事前に – 予めリネームしたやつをjarにパッケージしてお く ©2013 CloudBees, Inc. All Rights Reserved 43
    44. 44. うまくいかない場合もある • 文字列操作でパッケージ名をいじっている • META-INF/… • モジュールの公開APIから参照されている ©2013 CloudBees, Inc. All Rights Reserved 44
    45. 45. ©2013 CloudBees, Inc. All Rights Reserved mage © http://sfcitizen.com/blog/wp-content/uploads/2011/11/6302790910_c4eb865892_o-copy.jpg 45
    46. 46. ありがちなパターン class Foo { static final Foo INSTANCE = new Foo(); … } class Bar { void bar() { doSomethingWith(Foo.INSTANCE); } } ©2013 CloudBees, Inc. All Rights Reserved 46
    47. 47. 逆立ちしたって無理! class Foo { static Foo getInstance() { … } } class Bar { void bar() { doSomethingWith(Foo.INSTANCE); } } ©2013 CloudBees, Inc. All Rights Reserved 47
    48. 48. ©2013 CloudBees, Inc. All Rights Reserved 48
    49. 49. 書き換え方 class Foo { @AdaptField(name=“INSTANCE”) static Foo getInstance() { … } } Barのロード時に 書き換え class Bar { void bar() { doSomethingWith(Foo.INSTANCE); } } ©2013 CloudBees, Inc. All Rights Reserved 49
    50. 50. Bytecode Compatibility Transformer bit.ly/b-c-t class Foo { @AdaptField(name=“INSTANCE”) static Foo getInstance() { … } } foo.jar Foo.class 変換 定義 ©2013 CloudBees, Inc. All Rights Reserved 50
    51. 51. Bytecode Compatibility Transformer bit.ly/b-c-t • 実行時 – 変換定義 → byte[] transform(byte[] classFile) 独自class loader foo.jar bar.jar 変換 定義 ©2013 CloudBees, Inc. All Rights Reserved 51
    52. 52. 思わぬ落とし穴が! ©2013 CloudBees, Inc. All Rights Reserved 52
    53. 53. 思わぬ落とし穴 class Foo { static Foo INSTANCE; } class Bar extends Foo { void m() { System.out.println(INSTANCE); } Bar#INSTANCE } ©2013 CloudBees, Inc. All Rights Reserved 53
    54. 54. プログラム変換の利点 • 思い切った書き換え • 予期しない変更に対応 ©2013 CloudBees, Inc. All Rights Reserved 54
    55. 55. プログラム変換の欠点 • 独自のクラスローダが必要 ©2013 CloudBees, Inc. All Rights Reserved 55
    56. 56. http://commons.wikimedia.org/wiki/File:Light_Bulb.jpg ©2013 CloudBees, Inc. All Rights Reserved 56
    57. 57. 一分で学ぶInvokedynamic • Java7の新しい機能 • 実行時リンク • 静的リンクと同じ速度 void foo() { +リンカの為の追 int x = 5; 加情報 String y = “hello”; Object o = intとstringからObjectを返す何か(x,y) return o; } +リンカの名前 ©2013 CloudBees, Inc. All Rights Reserved 57
    58. 58. http://no-more-tears.kohsuke.org/ class Foo { void foo() { Project p = new Project(); Future<Build> f = p.scheduleBuild(); Build b = f.get(); … } } ©2013 CloudBees, Inc. All Rights Reserved 58
    59. 59. ビルド時にinvokedynamicに置き換え new Project() class Foo { Project.scheduleBuild() void foo() { Project p = [void→Project](this); Future<Build> f = [Project→Future] (p); Build b = [Future → Build](f); … Future.get() } } ©2013 CloudBees, Inc. All Rights Reserved 59
    60. 60. 実行時に適宜書き換え class Foo { void foo() { Project p = Project.create(); Future<Build> f = p.scheduleBuild(0); Build b = f.value; … } } ©2013 CloudBees, Inc. All Rights Reserved 60
    61. 61. まとめ 互換性とコードの進化は両立できる ©2013 CloudBees, Inc. All Rights Reserved 61

    ×