— boreal-kiss.com

Archive
Tag "ActionScript"

追記 2008/09/13

もう少し読みやすい文章(図付き)はこちら

追記 2008/09/16

すでにAdobe AIR Update Framework なんてものがあるじゃないか。。。仕組み的には同じっぽいのでまあ安心した。使い方は自動Update part2 (shin1)が詳しいのかな。

追記 2008/09/17

[AIR] Adobe AIR Update Frameworkを使ってみた – blog Boreal Kiss

___________________________

AIRアプリケーションのアップデート機能は汎用性が高いと思われたので専用のActionScriptライブラリを作った。このライブラリは、(このライブラリをインポート済みの)古いAIRアプリケーションから最新バージョン情報の有無をチェックして、もし最新バージョンがあるようならその場でアップデートを行う。ライブラリ以外に必要なのは以下の2ファイルで、それぞれweb上にアップロードしてあることが前提。

  • アプリケーションの最新バージョン
  • アプリケーション最新バージョンの設置先URLとそのバージョン情報を記述したXMLファイル

以下では上記の準備(1, 2)を行ってからライブラリの使い方(3)について説明している。

1. アプリケーション最新バージョンを作る

例えば最新バージョンのアプリケーション(latest_app.air, バージョン2.0と仮定)を以下のURLにアップロードしたと仮定。

http://example.com/latest_app.air

2. 最新バージョンチェック用のXMLファイルを作る

最新アプリケーションバージョンとそのファイル設置場所に関する情報を記述した以下のようなXMLファイル(update.xml)

<?xml version="1.0" encoding="UTF-8"?>
<application>
    <version>2.0</version>
    <download_url>http://example.com/latest_app.air</download_url>
</application>

を以下のURLにアップロードしたと仮定。

http://example.com/update.xml

3. ライブラリを使う

  1. ライブラリのダウンロード
  2. 古いAIRアプリケーション(例としてバージョン1.0)内にインポートしてAirUpdaterのインスタンスを作成。
    import com.borealkiss.fl.utils.AirUpdater;
    
    var updater:AirUpdater;
    updater = AirUpdater.getInstance("1.0",
                                     "http://example.com/update.xml",
                                     "version",
                                     "download_url");
  3. 上記引数は全てStringで一番前から順番に、(1)現在のアプリケーションバージョン、(2)バージョンチェック用XMLファイルのURL、(3)XMLファイル中の最新バージョンを記述しているノード名(<version>)、(4)XMLファイル中の最新バージョンのURLを記述しているノード名(<download_url>)、となっている。バージョンチェック用XMLファイル内のノード名は好き勝手つけれるが、必ずgetInstance()の引数名と一致させる必要がある。
  4. AirUpdater.check()でバージョンチェック開始。update.xmlの内容を解析して、現在のバージョンとXMLファイルに記述されたバージョンの数値の大小を比較して、(1)最新バージョンがある、(2)最新バージョンはない、(3)送信エラー(アクセス先URLにXMLファイルがない等)、の3通りの場合に対してイベントを返す。それぞれにイベントリスナーを設置。
    updater.check();
    
    //最新バージョンが存在する
    updater.addEventListener(AirUpdater.LATEST_VERSION_FOUND,onFound);
    
    //最新バージョンは存在しない(現在のものが最新)
    updater.addEventListener(AirUpdater.LATEST_VERSION_NOT_FOUND,onFound);
    
    //通信エラー
    updater.addEventListener(IOErrorEvent.IO_ERROR,onFound);
  5. あとは最新バージョンがある場合はAirUpdater.downloadUpdate()を行う。これで最新バージョンのアプリケーションをデスクトップにダウンロードし、現在立ち上げているアプリケーションを最新バージョンに書き換える。
    private function onFound(e:Event):void{
        switch(e.type){
            case AirUpdater.LATEST_VERSION_FOUND:
            updater.downloadUpdate();
            break;
                        
            case AirUpdater.LATEST_VERSION_NOT_FOUND:
            trace("No newer version found");
            break;
                        
            default:
            trace("Error");
    
    
        }
    }

その他注意点

  • 単純に数値の大きいものを最新バージョンと見なす。例えば上記の例の場合、1.0より2.0の方が最新ということになる。AirUpdaterに渡すバージョン情報とXMLファイル中のバージョン情報に数値以外が入っていると解析不能(例えばv2.5)。
  • アプリケーションの実際のバージョン情報を参照するわけではない。AirUpdaterに渡すバージョン数値とXMLファイル中のバージョン数値の大小を比較してアップデートを行う。
  • 最新バージョンをリリースするたびにバージョンチェック用のXMLファイルも修正する必要がある。
  • 同じアプリケーションID同士でないとアップデートできない。
Read More

Apache + Rails + Flex: FlashVarsを使う場合でレーダーチャートをFlexで作ったらファイルサイズ236Kb、同じものをActionScriptファイルのみで作ったら4Kbだったので驚愕。ためしに以下のような空のFlexアプリケーションを作ってみた。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
            width="200" height="200" backgroundColor="#FFFFFF">
</mx:Application>

ファイルサイズ148Kbだった。こんな感じ。

gradient

同じものをActionScriptクラスで作ったら4Kbだった。差ありすぎじゃねー。

package {
    import flash.display.GradientType;
    import flash.display.Sprite;
    import flash.geom.Matrix;

    [SWF(width="200",height="200")]

    public class GradientFlash extends Sprite{
        
        public function GradientFlash(){
            var w:Number = this.stage.stageWidth;
            var h:Number = this.stage.stageHeight;
            
            var mat:Matrix = new Matrix();
            mat.createGradientBox(w,h,Math.PI/2);
            var colors:Array = [0xFFFFFF, 0xDDDDDD];
            var alphas:Array = [1,1];
            var ratios:Array = [0x00,0xFF];
            graphics.beginGradientFill(GradientType.LINEAR,
                                    colors,alphas,ratios,mat);
            graphics.drawRect(0,0,w,h);
            graphics.endFill();
        }
    }
}

おまけ

Flexで背景のグラディエントをなくす場合は以下の通り。

backgroundGradientColors="[#FFFFFF,#FFFFFF]"
Read More

ActionScript 3.0 Design Patterns (Adobe Developer Library)

プログラムの設計思想と具体的な王道設計パターンについて豊富に例を挙げつつ説明してる本。内容自体はどのプログラミング言語(OOP)にも共通なのだけど、わざわざActionScriptを題材にしている点が貴重。ActionScriptに馴染みのある人は親近感を持ちやすいのではないかと思う。章ごとに各デザインパターンの性質、最低限の骨組み、簡単な例、少し複雑な例と書かれていて段階的に読めるからわかりやすかった。さらに章内容が独立しているので、例えばStrategy > Composite > Observer > Model-View-Controllerパターンと必要な部分だけを読むことで短期間でwebアプリケーション制作に役立つ設計技術が習得できそうな感じがする。個人的にはデザインパターン云々よりもそれ以前のOOPに関する知識・思想の理解が深まったのが収穫。タイトルのActionScriptとDesign Patternはおまけかな。

良書だと思うけど、難点も結構有り。デザインパターンの説明をする際に、日常生活の例を挙げて説明することが多いのだが、これが非常にわかりにくい(日本人的でないというのもあるかも)。まずは必要最低限のプログラムの骨格を提示するところから始まる方がよかったのではないかと思う。さらにプログラム全体のフローチャート図がたくさん挙げられるが、図の説明が全くないので理解不能。図は非常にわかりやすそうな絵柄をしているだけに残念で仕方ない。最後に、ソースの誤字脱字が半端なく多い(10-20どころではない)。typoというレベルではなく、引数が書いてなかったりさらには一行きれいさっぱり書かれていなかったりするものもありちょっとびびる。

こういう弱点もふまえてオススメの読み方としては、

  1. 第一章のOOP基本概念を読む
  2. 各デザインパターン章の「Minimalist example」以降を読む(それ以前は前置きが長い)
  3. 「Minimalist Example」で理解できなければそれ以前の概念部分を読む
  4. 各章最後部はプログラムが複雑になっていくだけなので、自分が必要だと思ったところまでで適当に切り上げる(逆に言うと説明不足ということはないほど例題が豊富)

追記 2008/07/19

最終章「13. Symmetric Proxy Pattern」は未読。

追記 2008/07/20

完読。

追記 2008/08/08

レーダーチャートによる評価レビューはこちら

Read More

Design PatternsのSingletonパターンついてのメモ。例えば以下のような勇者クラス(Yusha.as)を作成したとする。

package singleton{
    public class Yusha{
        public function sayHello():void{
            trace("僕が勇者だ");
        }
    }
}

RPGにおいて勇者は一般に物語中に一人しか存在しない。従って以下のように複数勇者が作成できるアプリケーション(Main.as)はRPG的に問題がある。

package {
    import flash.display.Sprite;
    import singleton.Yusha;

    public class Main extends Sprite{
        public function Main(){
            var y1:Yusha = new Yusha();
            var y2:Yusha = new Yusha();
            var y3:Yusha = new Yusha();

            y1.sayHello();//僕が勇者だ
            y2.sayHello();//僕が勇者だ
            y3.sayHello();//僕が勇者だ
        }
    }
}

そこでSingletonパターンを利用する。例えばCreating Mashups with Adobe Flex and AIRでは以下のような方法でインスタンスを唯一作成する方法が紹介されている。

package singleton{
    public class Yusha{
        
        private static var _instance:Yusha;
        
        public static function getInstance():Yusha{
            if (Yusha._instance == null){
                Yusha._instance = new Yusha();
            }
            return Yusha._instance;
        }
    }
}

この方法だとgetInstance()メソッドを使う限り勇者は複数作成されないが、以下のように直接Yushaクラスを作成された時に対処できなくなる。

//In Main.as
var y:Yusha = new Yusha();

Yushaクラスのコンストラクタをprivateにできれば一件落着だけど、ActionScript 3.0ではコンストラクタをprivateにできない。そこで同パッケージ内にDummyクラスを用意、それをYushaクラスの引数としてYushaクラスのコンストラクタにパッケージ外部からアクセスできないようにする(疑似private化)。

//Yusha.as
package singleton{
    public class Yusha{
        
        private static var _instance:Yusha;
        
        public function Yusha(d:Dummy){   
        }
        
        public static function getInstance():Yusha{
            if (Yusha._instance == null){
                Yusha._instance = new Yusha(new Dummy());
            }
            return Yusha._instance;
        }
        
        public function sayHello():void{
            trace("僕が勇者だ");
        }
    }
}
//Dummy.as
package singleton{
    internal class Dummy{
    }
}

これによりYushaクラスは作成時にDummyクラスを引数とし、さらにDummyクラスはsingletonパッケージ外部からアクセス不能なので、MainクラスからのYushaクラスの呼び出しはgetInstance()メソッドからのみ可能となる。

//In Main.as
var y1:Yusha = new Yusha();//Error
var y2:Yusha = new Yusha(new Dummy());//Error
var y3:Yusha = Yusha.getInstance();//OK

これにより勇者は唯一無二作成されることになりました。めでたしめでたし。

[関連記事]
Tanablog: 複数版 Singleton パターン

ActionScript 3.0 Design Patterns (Adobe Developer Library)

追記:2008/07/11

Dummyクラスはsingletonパッケージ内であればアクセス可能なので、記事タイトルの「疑似private」は「疑似internal」とした方が良いかもしれない。AS3で1ファイルに複数のクラスを定義する方法 – てっく煮ブログのように、DummyクラスをYushaクラスファイル内部に定義することで、Dummyクラスへのアクセスをprivate化することもできる。

Read More

various functions with different distributions for Math.random() « Pixeleroで興味深いことをやっていたのでFlashで実践。Math.random()の頻度分布は0以上1未満の範囲でだいたいフラットな形状になる。じゃあ例えばこれは?

f = (Math.random()+Math.random()+Math.random()+Math.random()) / 4

フラットな頻度分布にならず0.5近傍に頻度のピークがくる。なんでだろ。パっと理由が思いつかない。以下にデモとActioScriptソース等。横軸が関数の返す値、縦軸が頻度でスライダーをいじると関数形が変わります。

[Flash]ランダム関数頻度分布

[追記:2008/06/08]

言及されていた。[ActionScript3.0](標準)正規分布乱数 | moriBlog
中心極限定理というらしい。ちなみにMath.random()を10万回足し合わせて10万で割ると値はほぼ0.5。相対誤差0.1%未満になる。Math.random()だけを使って特定の数値を作ることが可能なわけだ。

[追記:2008/06/09 11:00]

Flex版を作成した。グラフ作成なんかはFlexで作るのが簡単でいいかも。ただファイルサイズがでかくなるのがネック。

[Flex]ランダム関数頻度分布その2

Read More

Papervision3D2.0(Great White)の割と最近のドキュメントがアップされてる(2008/04/22)。2.0は現段階でアルファ版でオフィシャルなドキュメンが出るのは当分先っぽかったのでたすかった。早速Fluidで専用ブラウザー化してドックに追加しといた。Fluid大活躍!Great White API

[関連リンク]
public var むらけん:Flasher; ASDoc GrateWhite(PaperVision3D2.0)

Read More

別ドメインの画像を読み込む場合のLoaderクラスについていくつかメモ、クロスドメインポリシーファイル(crossdomain.xml)の読み込み方法と画像ロード速度について。結果的には画像20枚2M程度だと気にするほどでもなかった。Loaderクラスの詳細はAS で別ドメインの画像を読み込むときの注意点 – てっく煮ブログが参考になる。まずLoaderクラスの重要な性質を列挙。

  • crossdomain.xmlの設定に無関係に画像のロードは可能。(その後の画像の扱いにかかわらず必ずload()を行う)
  • 画像を画像として扱う(ただ画像を表示する)場合にはcrossdomain.xmlによる許可は必要ない。つまりどこの画像であろうと表示は可能。
  • 画像をデータとして扱う(サイズを変更するetc.)場合にはcrossdomain.xmlの許可が必要。
  • デフォルトでcrossdomain.xmlを読まない。(URLLoaderクラスは読む)

Loaderクラスを用いて別ドメインの画像をデータとして扱う方法は以下の二種類。どちらもロード完了後、(crossdomain.xmlで許可されている場合)画像データにアクセスが可能になる。

  1. Security.loadPolicyFile()関数でcrossdomain.xmlを読み込ませ、swfファイルに許可があることを知らせてからロードを始める。(実際にはcrossdomain.xmlを読み込むまでロードは自動待機)
  2. //Ex., media.tumblr.com
    Security.loadPolicyFile("http://media.tumblr.com/crossdomain.xml");
    var loader:Loader = new Loader();
    var req:URLRequest = new URLRequest("http://media.tumblr.com/xxx.jpg");
    loader.load(req);
    
  3. LoaderContextクラスのcheckPolicyFileプロパティを用いて、ロードごとにcrossdomain.xmlを読むよう指示する。
  4. var context:LoaderContext = new LoaderContext(true);
    var loader:Loader = new Loader();
    var req:URLRequest = new URLRequest("http://media.tumblr.com/xxx.jpg");
    loader.load(req, context);
    

上記二種の違いは、ロード前にcrossdomain.xmlを読み込ませるか、ロードごとにcrossdomain.xmlを読み込ませるかの違いでなので、画像のロード数が増えると両者の実行速度に差がでてくるのではないかと予想される(Security.loadPolicyFile()を使った方が早そう)。実際に調べてみた。以下はFirefoxのキャッシュを消去してからmedia.tumblr.comの画像20枚(1.7M)を読み込ませた結果をFirebugで表示したもの。左列に読み込んだファイル名一覧、右側にそれぞれ表示完了までにかかった時間が表示されている。

  1. Security.loadPolicyFile()関数を用いた場合 (11.75s)
  2. Security.loadPolicyFile()関数を用いた場合

  3. LoaderContextクラスを用いた場合 (13.21s)
  4. LoaderContextクラスを用いた場合

Security.loadPolicyFile()関数で一回だけcrossdomain.xmlを読み込んだ場合の方が完了が早くなっているが、実験のインターネット環境がよくないのであまり信用しないでください。場合によってはLoaderContextクラスでロードごとにcrossdomain.xmlを読み込ませる場合の方が早かったりもしたのでなんともいえない。結論としては両者に大差なし。というかLoaderContextクラスを用いた場合でも二回目以降同じcrossdomain.xmlの読み込みはキャッシュ化されてるから差がでてないのかもしれない。面白いのは上記二つの方法でcrossdomain.xmlをロードするタイミングが異なること。Security.loadPolicyFile()関数を用いた場合全画像をロード後、LoaderContextクラスを用いた場合画像ロード前にcrossdomain.xmlが読み込まれていて、イメージと逆なので不思議。日本の安定している快適なインターネット環境で誰か実験するのを期待しつつ、以下実験のソースコード。

package {
    import flash.display.Sprite;
    import flash.display.Loader;
    import flash.events.Event;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.system.Security;
    
    public class LoadTest extends Sprite {
        public function LoadTest() {
            
            // 20 sexy images from media.tumblr.com
            var URLs:Array = [
            "http://media.tumblr.com/rJ6tpU7iW6y88so6TV84T1IG_500.jpg",
            "http://media.tumblr.com/z5Ki84RwK7e6bfe7zaGZCGUR_500.jpg",
            "http://media.tumblr.com/F2GLxhk3c6x444v1vma5BvIX_500.jpg",
            "http://media.tumblr.com/1gOzmif8i8oz2hz9GJ68aqBc_500.jpg",
            "http://media.tumblr.com/ky9cwXAKO8pyvjydMxesdNtr_500.jpg",
            "http://media.tumblr.com/7MmWU7n9s8l2gik4NKAlJjev_500.jpg",
            "http://media.tumblr.com/kcq2O9G2O8nggwncmiXj5Q8Z_500.jpg",
            "http://media.tumblr.com/5Vq1ESVSU8mn4n9raQLSFR6q_500.jpg",
            "http://media.tumblr.com/5Vq1ESVSU8mmwc4gekPbI9tl_500.jpg",
            "http://media.tumblr.com/0hxKQCklp2vdfmjiczTJn7v4_500.jpg",
            "http://media.tumblr.com/5Vq1ESVSU8mmtx38rZpmce95_500.jpg",
            "http://media.tumblr.com/0hxKQCklp2vdeqr003dmPyAo_500.jpg",
            "http://media.tumblr.com/0hxKQCklp4amrjwdD250Zy02_500.jpg",
            "http://media.tumblr.com/0hxKQCklp5kgx39bYjGWw6tB_500.jpg",
            "http://media.tumblr.com/0hxKQCklp6hoyaquSCmhzDXR_500.jpg",
            "http://media.tumblr.com/0hxKQCklp5xcqrjwzCIQggYR_500.jpg",
            "http://media.tumblr.com/0hxKQCklp6iz6is1QQJOQpi2_500.jpg",
            "http://media.tumblr.com/0hxKQCklp7swil3bMb5BHmae_500.jpg",
            "http://media.tumblr.com/FXGYbGlnr2pn7ftwSjgoCUtC_500.jpg",
            "http://media.tumblr.com/rJ6tpU7iW7smq0v5T9rpyo7w_500.jpg"];
            
            //loadModule1(URLs);
            loadModule2(URLs);
            
        }
        
        private function loadModule1(imgAry:Array):void{
            //Include crossdomain.xml from media.tumblr.com, once.
            Security.loadPolicyFile("http://media.tumblr.com/crossdomain.xml");
            
            var loaderAry:Array = new Array();
            
            for (var i:int=0; i<imgAry.length; i++){
                var loader:Loader = new Loader();
                var req:URLRequest = new URLRequest(imgAry[i]);
                loader.load(req);
                loader.contentLoaderInfo.addEventListener(Event.INIT,init);
                loaderAry.push(loader);
            }
        }
        
        private function loadModule2(imgAry:Array):void{      
            var loaderAry:Array = new Array();
            
            for (var i:int=0; i<imgAry.length; i++){
                var loader:Loader = new Loader();
                var req:URLRequest = new URLRequest(imgAry[i]);
                
                //Include crossdomain.xml per image (i.e., 20 times)
                var context:LoaderContext = new LoaderContext(true);
                loader.load(req,context);
                loader.contentLoaderInfo.addEventListener(Event.INIT,init);
                loaderAry.push(loader);
            }
        }
        
        private function init(e:Event):void{
            addChild(e.target.content);
        }
    }
}

Read More

ブログパーツでFLASHを別ドメインで表示させる時、読み込み外部ファイルがある場合はそのファイルのロケーションをURLで指定しないといけない(相対パス不可)。URLでの指定はディレクトリの変更があった場合に柔軟に対処できないので極力避けたい。じゃあSWFに埋め込んでしまおうと思い、XMLファイルの埋め込み方法で悩んだ。FlexのEmbedメタタグはデフォルトでXML形式のデータを理解しないので、形式を気にせずとりあえず読み込ませてその後XMLに強制変換してやる必要がある。

埋め込みに使用する MIME タイプは application/octet-stream として指定する必要があります。これにより、バイトデータは、解釈されずにそのまま埋め込まれます。

mx.core.ByteArrayAsset (Flex 3)- adobe

ソース内部で型を変換できるタイプのデータだったらなんでもいけるっぽい。XMLデータ埋め込みの例は以下の通り。

package {
    public class myClass{

        [Embed(source="mydata.xml", mimeType="application/octet-stream")]
        private var myData:Class;

        public function myClass(){
            var myXML:XML = new XML(new myData());
        }
    }
}
Read More

Keith PetersのBIT-101 Blogで配布されている画像等を魚眼レンズのように歪ませるカスタムクラスが手軽で面白い。例えばこの画像が

Hay Bails - Full Panoramic
Hay Bails – Full Panoramic on Flickr – Photo Sharing!

こうなる。

Hay Bails - Polar Distorted

歪ませるターゲットはDisplayObjectを継承していれば何でもOKなので、例えばテキストなんかにも適用できる。変形のターゲットを決めて(下の例の場合 var img:Bitmap)、render関数を用いて書き出し用BitmapData(下の例の場合 var bmpd:BitmapData)にレンダリングするだけ。面白いのはrender関数の第四引数の選び方。trueで画像の上側を円中心(例の写真の場合、空側を円中心)、falseで円外側に書き出す。ここの選び方で出来上がったイメージの雰囲気がガラっとかわる。

package {
    import flash.display.Sprite;
    import flash.display.BitmapData;
    import flash.display.Bitmap;
    import com.bit101.PolarDistort;

    public class PolarDistortDemo extends Sprite{
        
        [Embed(source="img/panoramic.jpg")]
        private var IMG:Class;
        
        public function PolarDistortDemo(){
            var img:Bitmap = new IMG();
            var pd:PolarDistort = new PolarDistort(img);
            var bmpd:BitmapData = new BitmapData(400,400,true,0x00FFFFFF);
            pd.render(bmpd,0,0,true);
            addChild(new Bitmap(bmpd));
        }
    }
}
Read More

理想的にはクラス内のプロパティは全てprivateにして、クラス外部からプロパティに直接アクセスさせず、かわりにgetter/setterメソッドと呼ばれる関数を用いてプロパティの参照や変更を行うことがOOPで推奨されてるらしい。直接アクセスされて不都合がある理由はよくわからんが、こういった慣例には従っておいた方がよいだろう。

オブジェクト指向プログラミングを実践する際には、クラスのプロパティに直接アクセスすることは推奨されません。クラスではよく、特定のプロパティの読取り用のアクセスに”get”メソッド、書込み用のアクセスに”set”メソッドを定義するということが行われます。

FN0311009 – 黙示的なget/setメソッド – Flash : テクニカルノート

ところでこのsetter/getterメソッドで使う関数名、なんと操作したいプロパティ名と同じ関数名をつけれない。これじゃ使い物にならんがな、と思ってたけどKeith Petersがプロパティ名の頭にアンダースコア(“_”)をつける方法で解決してた。例えばmessageというプロパティにsetter/getterメソッドを用いる場合、こんな感じ。

package{
    public class SomeClass{
        
        private var _message:String;
        
        public function SomeClass(){
            //Some codes here.
        }
        
        public function set message(s:String):void{
            _message = s;
        }
        
        public function get message():String{
            return _message;
        }
    }
}

これで、クラス外部でこのプロパティ値を変更したい場合はsetter関数経由で

var sc:SomeClass = new SomeClass();

//same as sc.message("Shut the fuck up!!");
sc.message = "Shut the fuck up!!";

クラス外部でこのプロパティ値を参照したい場合はgetter関数経由で

//same as trace(sc.message());
trace(sc.message);

なるほど、getter/setterメソッドを用いる場合のプロパティにはアンダースコアを付けるように決めてしまえば、以下のようにソースを見て混乱することもないし、しかもAS2をやってる人には馴染み深いしね。いいチップだ。

getter メソッドと setter メソッドは便利ですが、多用しすぎるとコードの保守が困難になる場合もあるので注意が必要です。

adobe – getter メソッドと setter メソッドについて

Read More