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

61
©2013 CloudBees, Inc. All Rights Reserved 1 ©2013 CloudBees, Inc. All Rights Reserved 互互互互互互互互互互互互 互互互互 CloudBees, Inc. [email protected] / @kohsukekawa

Upload: kohsuke-kawaguchi

Post on 08-Sep-2014

16 views

Category:

Technology


1 download

DESCRIPTION

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

TRANSCRIPT

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

©2013 CloudBees, Inc. All Rights Reserved 1©2013 CloudBees, Inc. All Rights Reserved

互換性とコード進化の両立川口耕介  CloudBees, [email protected] / @kohsukekawa

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

©2013 CloudBees, Inc. All Rights Reserved 2

分散・独立・並行開発

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

©2013 CloudBees, Inc. All Rights Reserved 3

モジュール化

http://commons.wikimedia.org/wiki/File:Close_up_of_Hand_Cut_Jigsaw_Puzzle.JPG

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

©2013 CloudBees, Inc. All Rights Reserved 4

実行時結合

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

©2013 CloudBees, Inc. All Rights Reserved 5

共通ライブラリ

ウェブアプリ X

モジュール A

ライブラリ v1.0

モジュール B

ライブラリ v2.0

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

©2013 CloudBees, Inc. All Rights Reserved 6

LinkageErrorAbstractMethodErrorNoClassDefFoundError

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

©2013 CloudBees, Inc. All Rights Reserved 7© いまいまさのぶ

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

©2013 CloudBees, Inc. All Rights Reserved 8

プラグイン機構

 コア 80+ のライブラリ様々なクラス

プラグイン プラグイン プラグイン …

プラグイン

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

©2013 CloudBees, Inc. All Rights Reserved 9

拡張ポイント

• コア

• プラグイン

interface Animal { void bark();}

@Extensionclass Dog implements Animal { void bark() { print(“ ワン !”); }}

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

©2013 CloudBees, Inc. All Rights Reserved 10

プラグインはコアをガンガン使う

Project p = …;Future<Build> f = p.scheduleBuild();Build b = f.get();

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

©2013 CloudBees, Inc. All Rights Reserved 11

この苦しみから解脱する方法はないかと

• お釈迦様の絵

• 徐々に蓄積してきたノウハウ・ツールを大公開

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

©2013 CloudBees, Inc. All Rights Reserved 12Image © http://sfcitizen.com/blog/wp-content/uploads/2011/11/6302790910_c4eb865892_o-copy.jpg

©2013 CloudBees, Inc. All Rights Reserved

基本編

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

©2013 CloudBees, Inc. All Rights Reserved 13

フィールドの隠蔽

class Point {int x,y;

}

class Point {int getX();int getY();void setX(int);void setY(int);

}

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

©2013 CloudBees, Inc. All Rights Reserved 14

インターフェース?抽象クラス?

interface Animal {void bark();

}

abstract class Animal {public abstractvoid bark();

}

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

©2013 CloudBees, Inc. All Rights Reserved 15

インターフェース?抽象クラス?

interface Animal {void bark();void bark(int

times);}

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

©2013 CloudBees, Inc. All Rights Reserved 16

インターフェース?抽象クラス?

abstract class Animal {public abstract void bark();

public void bark(int times) {for (int i=0; i<times; i++)

bark();}

}

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

©2013 CloudBees, Inc. All Rights Reserved 17

ピンポンパターン

abstract class Animal {@deprecatedpublic void bark() {

bark(1);}

public void bark(int times) {for (int i=0; i<times; i++)

bark();}

}

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

©2013 CloudBees, Inc. All Rights Reserved 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) {

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

©2013 CloudBees, Inc. All Rights Reserved 19

我慢してセッター

class Foo {void initXYZ(int x, int y, int z) { … }void initABC(String a, String b, String c)

{ … }}

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

©2013 CloudBees, Inc. All Rights Reserved 20

コンフィグパターン

class FooConfig {int x,y,z;String

a,b,c;}

abstract class Foo {Foo(FooConfig config)

{…

}}

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

©2013 CloudBees, Inc. All Rights Reserved 21Image © http://sfcitizen.com/blog/wp-content/uploads/2011/11/6302790910_c4eb865892_o-copy.jpg

©2013 CloudBees, Inc. All Rights Reserved

中級編

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

©2013 CloudBees, Inc. All Rights Reserved 22

パッケージ越しにアクセスするために public なんだけど、外部からは使わせたくない

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

©2013 CloudBees, Inc. All Rights Reserved 23

互換性用に @deprecated としたコードを次のステップに進めた

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

©2013 CloudBees, Inc. All Rights Reserved 24

俺俺アクセス

修飾子をつくる道具を

だして!

© 小学館

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

©2013 CloudBees, Inc. All Rights Reserved 25

http://kohsuke.org/access-modifier/

• 独自のアクセスチェッカを定義する

• サブタイプ、読み、書き、呼び出しなど6種類

class 俺俺アクセス extends AccessRestriction {

public void instantiated(loc,target,listener) {

listener.onError(null,loc,target+“ インスタンス生成

は禁止” );}

}

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

©2013 CloudBees, Inc. All Rights Reserved 26

http://kohsuke.org/access-modifier/

• 通常のアクセス修飾子と共に使う

• Maven プラグインがクラスファイルを検査

@Restricted( 俺俺アクセス .class)public class Foo {

public String value;}

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

©2013 CloudBees, Inc. All Rights Reserved 27

バイナリ互換性

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

©2013 CloudBees, Inc. All Rights Reserved 28

バイナリ互換性を活用

interface Animal {void bark();

}class Dog implements Animal {

void bark() { print(“ ワン” ); }}

Animal a = new Dog();a.bark();

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

©2013 CloudBees, Inc. All Rights Reserved 29

バイナリ互換性を活用

interface Animal {void bark(int n);

}class Dog implements Animal{

void bark() { print(“ ワン” ); }}

Animal a = new Dog();a.bark(3);

1. new Dog() で InstantiationError2. Dog→Animal のキャストで

ClassCastException3. a.bark(3) で AbstractMethodError4.その他

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

©2013 CloudBees, Inc. All Rights Reserved 30

try {a.bark(3);

} catch (AbstractMethodError e) {// 互換性モード…

}

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

©2013 CloudBees, Inc. All Rights Reserved 31

ジェネリクス

class Foo {List<Object> getChildren() { … }

}

class Foo {List<String> getChildren() { … }

}

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

©2013 CloudBees, Inc. All Rights Reserved 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 { … }

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

©2013 CloudBees, Inc. All Rights Reserved 33

ジェネリクス型変更のルール

• メソッド・フィールドの Erasure が変わっていなければ OK– T<X,Y,…> → T– T extends X → X–…

• 検査してくれるツールをいつかは書きたい

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

©2013 CloudBees, Inc. All Rights Reserved 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

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

©2013 CloudBees, Inc. All Rights Reserved 35

参照解決のルール

• メソッドへの参照–クラス名: java.lang.String–メソッド名: indexOf–戻り値型: int–パラメータ型: java.lang.String,int

• 関連事項–アクセス修飾子–例外

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

©2013 CloudBees, Inc. All Rights Reserved 36

コード進化のパターン

class Foo {X get() { … }

}

class Foo {Y get() { … }

}class Y extends X { … }

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

©2013 CloudBees, Inc. All Rights Reserved 37

コード進化のパターン

class Foo {X get() { … }

}

class Foo {Y get() { … }X get() { … }

}

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

©2013 CloudBees, Inc. All Rights Reserved 38

Bridge Method Injector プロジェクト bit.ly/b-m-i

class Foo {@WithBridgeMethods(X.class)Y get() { … }

}

class Foo {Y get() { … }X get() { Y y=get(); return y; }

}

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

©2013 CloudBees, Inc. All Rights Reserved 39

応用

class Foo {@WithBridgeMethods(value=X.class,

castRequired=true)Object get() { … }

}

class Foo {Object get() { … }X get() { Object o=get(); return (X)o; }

}

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

©2013 CloudBees, Inc. All Rights Reserved 40

自分はルールを守っていても

モジュール A

モジュール B

ライブラリ 1.0

ライブラリ 2.0

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

©2013 CloudBees, Inc. All Rights Reserved 41

シェーディング / パッケージ・リネーミング

package org.jenkinsci.foo;

import org.acme.A;import org.acme.B;

class Foo {private List<A> a = …;void bar() { new B().b(); }

}

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

©2013 CloudBees, Inc. All Rights Reserved 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(); }

}

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

©2013 CloudBees, Inc. All Rights Reserved 43

シェーディング / パッケージ・リネーミング

• 事後に–maven-shade-plugin

• 事前に–予めリネームしたやつを jar にパッケージして

おく

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

©2013 CloudBees, Inc. All Rights Reserved 44

うまくいかない場合もある

• 文字列操作でパッケージ名をいじっている• META-INF/…

• モジュールの公開 API から参照されている

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

©2013 CloudBees, Inc. All Rights Reserved 45Image © http://sfcitizen.com/blog/wp-content/uploads/2011/11/6302790910_c4eb865892_o-copy.jpg

©2013 CloudBees, Inc. All Rights Reserved

上級編

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

©2013 CloudBees, Inc. All Rights Reserved 46

ありがちなパターン

class Foo {static final Foo INSTANCE = new Foo();…

}

class Bar {void bar() {

doSomethingWith(Foo.INSTANCE);}

}

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

©2013 CloudBees, Inc. All Rights Reserved 47

逆立ちしたって無理!

class Foo {static Foo getInstance() { … }

}

class Bar {void bar() {

doSomethingWith(Foo.INSTANCE);}

}

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

©2013 CloudBees, Inc. All Rights Reserved 48

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

©2013 CloudBees, Inc. All Rights Reserved 49

書き換え方

class Foo {@AdaptField(name=“INSTANCE”)static Foo getInstance() { … }

}

class Bar {void bar() {

doSomethingWith(Foo.INSTANCE);}

}

Bar のロード時に書き換え

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

©2013 CloudBees, Inc. All Rights Reserved 50

Bytecode Compatibility Transformer bit.ly/b-c-t

class Foo {@AdaptField(name=“INSTANCE”)static Foo getInstance() { … }

}

foo.jar

Foo.class 変換定義

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

©2013 CloudBees, Inc. All Rights Reserved 51

独自 class loader

Bytecode Compatibility Transformer bit.ly/b-c-t• 実行時–変換定義 → byte[] transform(byte[]

classFile)

foo.jar bar.jar

変換定義

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

©2013 CloudBees, Inc. All Rights Reserved 52

思わぬ落とし穴が!

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

©2013 CloudBees, Inc. All Rights Reserved 53

思わぬ落とし穴

class Foo {static Foo INSTANCE;

}class Bar extends Foo {

void m() {System.out.println(INSTANCE);

}} Bar#INSTANCE

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

©2013 CloudBees, Inc. All Rights Reserved 54

プログラム変換の利点

• 思い切った書き換え• 予期しない変更に対応

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

©2013 CloudBees, Inc. All Rights Reserved 55

プログラム変換の欠点

• 独自のクラスローダが必要

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

©2013 CloudBees, Inc. All Rights Reserved 56http://commons.wikimedia.org/wiki/File:Light_Bulb.jpg

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

©2013 CloudBees, Inc. All Rights Reserved 57

一分で学ぶ Invokedynamic

• Java7 の新しい機能• 実行時リンク• 静的リンクと同じ速度

void foo() {int x = 5;String y = “hello”;Object o = int と string から Object を返す何

か (x,y)return o;

}

+リンカの為の追加情報

+リンカの名前

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

©2013 CloudBees, Inc. All Rights Reserved 58

http://no-more-tears.kohsuke.org/

class Foo {void foo() {

Project p = new Project();Future<Build> f = p.scheduleBuild();Build b = f.get();…

}}

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

©2013 CloudBees, Inc. All Rights Reserved 59

ビルド時に invokedynamic に置き換え

class Foo {void foo() {

Project p = [void→Project](this);Future<Build> f = [Project→Future] (p);Build b = [Future → Build](f);…

}}

new Project()

Project.scheduleBuild()

Future.get()

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

©2013 CloudBees, Inc. All Rights Reserved 60

実行時に適宜書き換え

class Foo {void foo() {

Project p = Project.create();Future<Build> f = p.scheduleBuild(0);Build b = f.value;…

}}

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

©2013 CloudBees, Inc. All Rights Reserved 61

まとめ

互換性とコードの進化は両立できる