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

Post on 08-Sep-2014

16 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

TRANSCRIPT

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

互換性とコード進化の両立川口耕介  CloudBees, Inc.kk@kohsuke.org / @kohsukekawa

©2013 CloudBees, Inc. All Rights Reserved 2

分散・独立・並行開発

©2013 CloudBees, Inc. All Rights Reserved 3

モジュール化

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

©2013 CloudBees, Inc. All Rights Reserved 4

実行時結合

©2013 CloudBees, Inc. All Rights Reserved 5

共通ライブラリ

ウェブアプリ X

モジュール A

ライブラリ v1.0

モジュール B

ライブラリ v2.0

©2013 CloudBees, Inc. All Rights Reserved 6

LinkageErrorAbstractMethodErrorNoClassDefFoundError

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

©2013 CloudBees, Inc. All Rights Reserved 8

プラグイン機構

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

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

プラグイン

©2013 CloudBees, Inc. All Rights Reserved 9

拡張ポイント

• コア

• プラグイン

interface Animal { void bark();}

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

©2013 CloudBees, Inc. All Rights Reserved 10

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

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

©2013 CloudBees, Inc. All Rights Reserved 11

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

• お釈迦様の絵

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

©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

基本編

©2013 CloudBees, Inc. All Rights Reserved 13

フィールドの隠蔽

class Point {int x,y;

}

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

}

©2013 CloudBees, Inc. All Rights Reserved 14

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

interface Animal {void bark();

}

abstract class Animal {public abstractvoid bark();

}

©2013 CloudBees, Inc. All Rights Reserved 15

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

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

times);}

©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();}

}

©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();}

}

©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) {

©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)

{ … }}

©2013 CloudBees, Inc. All Rights Reserved 20

コンフィグパターン

class FooConfig {int x,y,z;String

a,b,c;}

abstract class Foo {Foo(FooConfig config)

{…

}}

©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

中級編

©2013 CloudBees, Inc. All Rights Reserved 22

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

©2013 CloudBees, Inc. All Rights Reserved 23

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

©2013 CloudBees, Inc. All Rights Reserved 24

俺俺アクセス

修飾子をつくる道具を

だして!

© 小学館

©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+“ インスタンス生成

は禁止” );}

}

©2013 CloudBees, Inc. All Rights Reserved 26

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

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

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

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

public String value;}

©2013 CloudBees, Inc. All Rights Reserved 27

バイナリ互換性

©2013 CloudBees, Inc. All Rights Reserved 28

バイナリ互換性を活用

interface Animal {void bark();

}class Dog implements Animal {

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

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

©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.その他

©2013 CloudBees, Inc. All Rights Reserved 30

try {a.bark(3);

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

}

©2013 CloudBees, Inc. All Rights Reserved 31

ジェネリクス

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

}

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

}

©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 { … }

©2013 CloudBees, Inc. All Rights Reserved 33

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

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

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

©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

©2013 CloudBees, Inc. All Rights Reserved 35

参照解決のルール

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

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

©2013 CloudBees, Inc. All Rights Reserved 36

コード進化のパターン

class Foo {X get() { … }

}

class Foo {Y get() { … }

}class Y extends X { … }

©2013 CloudBees, Inc. All Rights Reserved 37

コード進化のパターン

class Foo {X get() { … }

}

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

}

©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; }

}

©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; }

}

©2013 CloudBees, Inc. All Rights Reserved 40

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

モジュール A

モジュール B

ライブラリ 1.0

ライブラリ 2.0

©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(); }

}

©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(); }

}

©2013 CloudBees, Inc. All Rights Reserved 43

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

• 事後に–maven-shade-plugin

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

おく

©2013 CloudBees, Inc. All Rights Reserved 44

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

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

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

©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

上級編

©2013 CloudBees, Inc. All Rights Reserved 46

ありがちなパターン

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

}

class Bar {void bar() {

doSomethingWith(Foo.INSTANCE);}

}

©2013 CloudBees, Inc. All Rights Reserved 47

逆立ちしたって無理!

class Foo {static Foo getInstance() { … }

}

class Bar {void bar() {

doSomethingWith(Foo.INSTANCE);}

}

©2013 CloudBees, Inc. All Rights Reserved 48

©2013 CloudBees, Inc. All Rights Reserved 49

書き換え方

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

}

class Bar {void bar() {

doSomethingWith(Foo.INSTANCE);}

}

Bar のロード時に書き換え

©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 変換定義

©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

変換定義

©2013 CloudBees, Inc. All Rights Reserved 52

思わぬ落とし穴が!

©2013 CloudBees, Inc. All Rights Reserved 53

思わぬ落とし穴

class Foo {static Foo INSTANCE;

}class Bar extends Foo {

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

}} Bar#INSTANCE

©2013 CloudBees, Inc. All Rights Reserved 54

プログラム変換の利点

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

©2013 CloudBees, Inc. All Rights Reserved 55

プログラム変換の欠点

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

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

©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;

}

+リンカの為の追加情報

+リンカの名前

©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();…

}}

©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()

©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;…

}}

©2013 CloudBees, Inc. All Rights Reserved 61

まとめ

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

top related