Java 23種設計模式 - 結構型模式7種
1 適配器模式
適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口不匹配而無法在一起工作的兩個類能夠在一起工作。
優點
- 將目標類和適配者類解耦
- 增加了類的透明性和復用性,將具體的實現封裝在適配者類中,對于客戶端類來說是透明的,而且提高了適配者的復用性
- 靈活性和擴展性都非常好,符合開閉原則
1.1 例子
整個流程就是通過 SmallToBig(適配器) 把 SmallPort(適配者) 轉換為 BigPort(目標) 的子類
/*** 投影儀支持的大口*/
public interface BigPort {public void userBigPort();//使用的大口
}
/*** 電腦的小口*/
public interface SmallPort {public void userSmallPort();//使用小口
}
/*** 適配器模式*/
public class SmallToBig implements BigPort{private SmallPort smallPort;//小口public SmallToBig(SmallPort smallPort){//獲得小口this.smallPort=smallPort;}public void userBigPort() {this.smallPort.userSmallPort(); //使用小口}}
public class Client {public static void main(String[] args) {SmallPort smallPort = new SmallPort() {//電腦自帶小口public void userSmallPort() {System.out.println("使用的是電腦小口");}};//需要一個大口才可以投影,小口轉換為大口BigPort bigPort=new SmallToBig(smallPort);bigPort.userBigPort();//電腦小口工作中 實現了適配}
}
2 裝飾器模式(Decorator Pattern)
通常給對象添加功能,要么直接修改對象添加相應的功能,要么派生子類來擴展,抑或是使用對象組合的方式。顯然,直接修改對應的類的方式并不可取,在面向對象的設計中,我們應該盡量使用組合對象而不是繼承對象來擴展和復用功能,裝飾器模式就是基于對象組合的方式的。
裝飾器模式以對客戶端透明的方式動態地給一個對象附加上了更多的責任。換言之,客戶端并不會角色對象在裝飾前和裝飾后有什么不同。裝飾器模式可以在不用創建更多子類的情況下,將對象的功能加以擴展。
Decorator裝飾器,顧名思義,就是動態地給一個對象添加一些額外的職責,就好比為房子進行裝修一樣。因此,裝飾器模式具有如下的特征:
- 它必須具有一個裝飾的對象。
- 它必須擁有與被裝飾對象相同的接口。
- 它可以給被裝飾對象添加額外的功能。
用一句話總結就是:保持接口,增強性能。
裝飾器通過包裝一個裝飾對象來擴展其功能,而又不改變其接口,這實際上是基于對象的適配器模式的一種變種。它與對象的適配器模式的異同點如下。
- 相同點:都擁有一個目標對象。
- 不同點:適配器模式需要實現另外一個接口,而裝飾器模式必須實現該對象的接口。
2.1 例子
現在有這么一個場景:
- 有一批廚師,簡單點吧,就全是中國廚師,他們有一個共同的動作是做晚飯
- 這批廚師做晚飯前的習慣不同,有些人喜歡做晚飯前洗手、有些人喜歡做晚飯前洗頭
public interface Cook {public void cookDinner();
}
public class ChineseCook implements Cook {@Overridepublic void cookDinner() {System.out.println("中國人做晚飯");}}
public abstract class FilterCook implements Cook {protected Cook cook;
}
public class WashHandsCook extends FilterCook {public WashHandsCook(Cook cook) {this.cook = cook;}@Overridepublic void cookDinner() {System.out.println("先洗手");cook.cookDinner();}}
public class WashHearCook extends FilterCook {public WashHearCook(Cook cook) {this.cook = cook;}@Overridepublic void cookDinner() {System.out.println("先洗頭");cook.cookDinner();}}
@Test
public void testDecorate() {Cook cook0 = new WashHandsCook(new ChineseCook());Cook cook1 = new WashHearCook(new ChineseCook());cook0.cookDinner();cook1.cookDinner();
}
結果:
先洗手
中國人做飯
先洗頭
中國人做飯
簡單的一個例子,實現了裝飾器模式的兩個功能點:
- 客戶端只定義了Cook接口,并不關心具體實現
- 給Chinese增加上了洗頭和洗手的動作,且洗頭和洗手的動作,可以給其他國家的廚師類復用
2.2 字節輸入流InputStream
InputStream是一個頂層的接口,文章開頭就說,裝飾器模式是繼承關系的一種替代方案,看一下為什么:
- InputStream假設這里寫了兩個實現類,FileInputStream,ObjectInputStream分別表示文件字節輸入流,對象字節輸入流
- 現在我要給這兩個輸入流加入一點緩沖功能以提高輸入流效率,使用繼承的方式,那么就寫一個BufferedInputStream,繼承FileInputStream,ObjectInputStream,給它們加功能
- 現在我有另外一個需求,需要給這兩個輸入流加入一點網絡功能,那么就寫一個SocketInputStream,繼承繼承FileInputStream,ObjectInputStream,給它們加功能
這樣就導致兩個問題:
- 因為我要給哪個類加功能就必須繼承它,比如我要給FileInputStream,ObjectInputStream加上緩沖功能、網絡功能就得擴展出2*2=4個類,更多的以此類推,這樣勢必導致類數量不斷膨脹
- 代碼無法復用,給FileInputStream,ObjectInputStream加入緩沖功能,本身代碼應該是一樣的,現在卻必須繼承完畢后把一樣的代碼重寫一遍,多此一舉,代碼修改的時候必須修改多個地方,可維護性很差
所以,這個的時候我們就想到了一種解決方案:
- 在要擴展的類比如BufferedInputStream中持有一個InputStream的引用,在BufferedInputStream調用InputStream中的方法,這樣擴展的代碼就可以復用起來
- 將BufferedInputStream作為InputStream的子類,這樣客戶端只知道我用的是InputStream而不需要關心具體實現,可以在客戶端不知情的情況下,擴展InputStream的功能,加上緩沖功能
這就是裝飾器模式簡單的由來,一切都是為了解決實際問題而誕生。下一步,根據UML圖,我們來劃分一下裝飾器模式的角色。
- InputStream是一個抽象構件角色:
- ByteArrayInputStream、FileInputStream、ObjectInputStream、PipedInputStream都是具體構建角色
- FilterInputStream無疑就是一個裝飾角色,因為FilterInputStream實現了InputStream內的所有抽象方法并且持有一個InputStream的引用
- 具體裝飾角色就是InflaterInputStream、BufferedInputStream、DataInputStream
搞清楚具體角色之后,我們就可以這么寫了
public static void main(String[] args) throws Exception
{File file = new File("D:/aaa.txt");InputStream in0 = new FileInputStream(file);InputStream in1 = new BufferedInputStream(new FileInputStream(file)); InputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
}
我們這里實例化出了三個InputStream的實現類:
- in0這個引用指向的是new出來的FileInputStream,這里簡單構造出了一個文件字節輸入流
- in1這個引用指向的是new出來的BufferedInputStream,它給FileInputStream增加了緩沖功能,使得FileInputStream讀取文件的內容保存在內存中,以提高讀取的功能
- in2這個引用指向的是new出來的DataInputStream,它也給FileInputStream增加了功能,因為它有DataInputStream和BufferedInputStream兩個附加的功能
同理,我要給ByteArrayInputStream、ObjectInputStream增加功能,也可以使用類似的方法,整個過程中,最重要的是要理解幾個問題:
- 哪些是具體構建角色、哪些是具體裝飾角色,尤其是后者,區分的關鍵就是,角色中是否持有頂層接口的引用
- 每個具體裝飾角色有什么作用,因為只有知道每個具體裝飾角色有什么作用后,才可以知道要裝飾某個功能需要用哪個具體裝飾角色
- 使用構造方法的方式將類進行組合,給具體構建角色加入新的功能
2.3 字符輸入流Reader
根據UML,分析一下每個角色:
- 抽象構建角色,毫無疑問,由Reader來扮演,它是一個抽象類,沒有具體功能
- 具體構建角色,由InputStreamReader、CharArrayReader、PipedReader、StringReader來扮演
- 裝飾角色,由FilterReader來扮演,BufferedReader是Reader的子類,且持有Reader的引用,因此這里的BufferedReader是可以被認為是一個裝飾角色的。
- 具體裝飾角色,BufferedReader上面提到了扮演了裝飾角色,但是也可以被認為是一個具體裝飾角色。除了BufferedReader,具體裝飾角色還有PushbackReader。
FileReader盡管也在第三行,但是FileReader構不成一個具體裝飾角色,因為它不是BufferedReader的子類也不是FilterReader的子類,不持有Reader的引用。
2.4 半透明裝飾器模式與全透明裝飾器模式
再說一下半透明裝飾器模式與全透明裝飾器模式,它們的區別是:
- 對于半透明裝飾器模式,裝飾后的類未必有和抽象構件角色同樣的接口方法,它可以有自己擴展的方法
- 對于全透明裝飾器模式,裝飾后的類有著和抽象構件角色同樣的接口方法, 全透明裝飾器模式是一種比較理想主義的想法,現實中不太可能出現。
比如BufferedInputStream吧,我把FileInputStream裝飾為BufferedInputStream,難道BufferedInputStream就完全沒有自己的行為?比如返回緩沖區的大小、清空緩沖區(這里只是舉個例子,實際BufferedInputStream是沒有這兩個動作的),這些都是InputStream本身不具備的,因為InputStream根本不知道緩沖區這個概念,它只知道定義讀數據相關方法。
所以,更多的我們是采用半透明的裝飾器模式,即 允許裝飾后的類中有屬于自己的方法,因此,前面的I/O代碼示例可以這么改動:
public static void main(String[] args) throws Exception
{File file = new File("D:/aaa.txt");FileInputStream in0 = new FileInputStream(file);BufferedInputStream in1 = new BufferedInputStream(new FileInputStream(file)); DataInputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
}
2.5 裝飾器模式的優缺點
優點
- 裝飾器模式與繼承關系的目的都是要擴展對象的功能,但是裝飾器模式可以提供比繼承更多的靈活性。裝飾器模式允許系統動態決定貼上一個需要的裝飾,或者除掉一個不需要的裝飾。繼承關系是不同,繼承關系是靜態的,它在系統運行前就決定了
- 通過使用不同的具體裝飾器以及這些裝飾類的排列組合,可以創造出很多不同行為的組合。
缺點
由于使用裝飾器模式,可以比使用繼承關系需要較少數目的類。使用較少的類,當然使設計比較易于進行。但是另一方面,由于使用裝飾器模式會產生比使用繼承關系更多的對象,更多的對象會使得查錯變得困難,特別是這些對象看上去都很像。
2.6 裝飾器模式和適配器模式的區別
其實適配器模式也是一種包裝(Wrapper)模式,它們看似都是起到包裝一個類或對象的作用,但是它們使用的目的非常不一樣:
- 適配器模式的意義是要將一個接口轉變成另外一個接口,它的目的是通過改變接口來達到重復使用的目的
- 裝飾器模式不要改變被裝飾對象的接口,而是恰恰要保持原有的接口,增強原有接口的功能,或者改變原有對象的處理方法而提升性能
3 代理模式
代理模式的定義很簡單: 給某一對象提供一個代理對象,并由代理對象控制對原對象的引用。
3.1 角色
一個客戶不想或者不能夠直接引用一個對象,可以通過代理對象在客戶端和目標對象之間起到中介作用。代理模式中的角色有:
- 抽象對象角色
聲明了目標對象和代理對象的共同接口,這樣一來在任何可以使用目標對象的地方都可以使用代理對象
- 目標對象角色
定義了代理對象所代表的目標對象
- 代理對象角色
代理對象內部含有目標對象的引用,從而可以在任何時候操作目標對象;代理對象提供一個與目標對象相同的接口,以便可以在任何時候替代目標對象
3.2 spring aop
在Spring的AOP編程中:
- 如果加入容器的目標對象有實現接口,用JDK代理
- 如果目標對象沒有實現接口,用Cglib代理。
3.3 靜態代理
接口:IUserDao.java
public interface IUserDao {void save();
}
目標對象:UserDao.java
public class UserDao implements IUserDao {public void save() {System.out.println("----已經保存數據!----");}
}
代理對象:UserDaoProxy.java
public class UserDaoProxy implements IUserDao{//接收保存目標對象private IUserDao target;public UserDaoProxy(IUserDao target){this.target=target;}public void save() {System.out.println("開始事務...");target.save();//執行目標對象的方法System.out.println("提交事務...");}
}
靜態代理總結:
- 可以做到在不修改目標對象的功能前提下,對目標功能擴展.
- 缺點:因為代理對象需要與目標對象實現一樣的接口,所以會有很多代理類,類太多.同時,一旦接口增加方法,目標對象與代理對象都要維護.
3.4 動態代理
動態代理有以下特點:
- 代理對象,不需要實現接口
- 代理對象的生成,是利用JDK的API,動態的在內存中構建代理對象(需要我們指定創建代理對象/目標對象實現的接口的類型)
- 動態代理也叫做:JDK代理,接口代理
JDK中生成代理對象的API
代理類所在包:java.lang.reflect.Proxy
JDK實現代理只需要使用newProxyInstance
方法,但是該方法需要接收三個參數,完整的寫法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意該方法是在Proxy類中是靜態方法,且接收的三個參數依次為:
ClassLoader loader
:指定當前目標對象使用類加載器,獲取加載器的方法是固定的Class<?>[] interfaces
:目標對象實現的接口的類型,使用泛型方式確認類型InvocationHandler
:事件處理,執行目標對象的方法時,會觸發事件處理器的方法,會把當前執行目標對象的方法作為參數傳入
接口類IUserDao.java以及接口實現類,目標對象UserDao是一樣的,沒有做修改.在這個基礎上,增加一個代理工廠類(ProxyFactory.java),將代理類寫在這個地方,然后在測試類(需要使用到代理的代碼)中先建立目標對象和代理對象的聯系,然后代用代理對象的中同名方法
/*** 創建動態代理對象* 動態代理不需要實現接口,但是需要指定接口類型*/
public class ProxyFactory{//維護一個目標對象private Object target;public ProxyFactory(Object target){this.target=target;}//給目標對象生成代理對象public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("開始事務2");//執行目標對象方法Object returnValue = method.invoke(target, args);System.out.println("提交事務2");return returnValue;}});}}
/*** 測試類*/
public class App {public static void main(String[] args) {// 目標對象IUserDao target = new UserDao();// 【原始的類型 class cn.itcast.b_dynamic.UserDao】System.out.println(target.getClass());// 給目標對象,創建代理對象IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();// class $Proxy0 內存中動態生成的代理對象System.out.println(proxy.getClass());// 執行方法 【代理對象】proxy.save();}
}
總結:代理對象不需要實現接口,但是目標對象一定要實現接口,否則不能用動態代理
3.5 Cglib代理
上面的靜態代理和動態代理模式都是要求目標對象是實現一個接口的目標對象,但是有時候目標對象只是一個單獨的對象,并沒有實現任何的接口,這個時候就可以使用以目標對象子類的方式類實現代理,這種方法就叫做: Cglib代理
Cglib代理,也叫作子類代理或繼承代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展.
- JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口,如果想代理沒有實現接口的類,就可以使用Cglib實現.
- Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
- Cglib包的底層是通過使用一個小而塊的字節碼處理框架ASM來轉換字節碼并生成新的類.不鼓勵直接使用ASM,因為它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉.
Cglib子類代理實現方法:
- 需要引入cglib的jar文件,但是Spring的核心包中已經包括了Cglib功能,所以直接引入
pring-core-3.2.5.jar
即可. - 引入功能包后,就可以在內存中動態構建子類
- 代理的類不能為final,否則報錯
- 目標對象的方法如果為final/static,那么就不會被攔截,即不會執行目標對象額外的業務方法.
/*** 目標對象,沒有實現任何接口*/
public class UserDao {public void save() {System.out.println("----已經保存數據!----");}
}
/*** Cglib子類代理工廠* 對UserDao在內存中動態構建一個子類對象*/
public class ProxyFactory implements MethodInterceptor{//維護目標對象private Object target;public ProxyFactory(Object target) {this.target = target;}//給目標對象創建一個代理對象public Object getProxyInstance(){//1.工具類Enhancer en = new Enhancer();//2.設置父類en.setSuperclass(target.getClass());//3.設置回調函數en.setCallback(this);//4.創建子類(代理對象)return en.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("開始事務...");//執行目標對象的方法Object returnValue = method.invoke(target, args);System.out.println("提交事務...");return returnValue;}
}
/*** 測試類*/
public class App {@Testpublic void test(){//目標對象UserDao target = new UserDao();//代理對象UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();//執行代理對象的方法proxy.save();}
}
4 外觀模式(Facade Pattern)
用于隱藏系統的復雜性,用戶客戶端與訪問者之間,通過一個外觀類,對客戶端屏蔽復雜的子系統調用。這種類型的設計模式屬于結構型模式。
4.1 例子
- Facade: 外觀角色
- SubSystem:子系統角色
public class SystemA {public void operationA(){System.out.println("operation a...");}
}public class SystemB {public void operationB() {System.out.println("operation b...");}
}public class SystemC {public void operationC() {System.out.println("operation c...");}
}
public class Facade {public void wrapOperation() {SystemA a = new SystemA();a.operationA();SystemB b = new SystemB();b.operationB();SystemC c = new SystemC();c.operationC();}
}
public class Client {public static void main(String[] args) {Facade facade = new Facade();facade.wrapOperation();}
}
result:
operation a...
operation b...
operation c...
4.2 模式分析
根據“單一職責原則”,在軟件中將一個系統劃分為若干個子系統有利于降低整個系統的復雜性,一個常見的設計目標是使子系統間的通信和相互依賴關系達到最小,而達到該目標的途徑之一就是引入一個外觀對象,它為子系統的訪問提供了一個簡單而單一的入口。
- 外觀模式也是“迪米特法則”的體現,通過引入一個新的外觀類可以降低原有系統的復雜度,同時降低客戶類與子系統類的耦合度。
- 外觀模式要求一個子系統的外部與其內部的通信通過一個統一的外觀對象進行,外觀類將客戶端與子系統的內部復雜性分隔開,使得客戶端只需要與外觀對象打交道,而不需要與子系統內部的很多對象打交道。
- 外觀模式的目的在于降低系統的復雜程度。
- 外觀模式從很大程度上提高了客戶端使用的便捷性,使得客戶端無須關心子系統的工作細節,通過外觀角色即可調用相關功能。
外觀模式的缺點
- 不能很好地限制客戶使用子系統類,如果對客戶訪問子系統類做太多的限制則減少了可變性和靈活性。
- 在不引入抽象外觀類的情況下,增加新的子系統可能需要修改外觀類或客戶端的源代碼,違背了“開閉原則”。
5 橋接模式 Bridge Pattern
將抽象化與實現化解耦,使得二者可以獨立變化,例如我們常用的JDBC橋DriverManager一樣,JDBC進行連接數據庫的時候,在各個數據庫之間進行切換,基本不需要動太多的代碼,甚至絲毫不用動,原因就是JDBC提供統一接口,每個數據庫提供各自的實現,用一個叫做數據庫驅動的程序來橋接就行了。
5.1 例子
public interface Sourceable { public void method();
}
public class SourceSub1 implements Sourceable { @Override public void method() { System.out.println("this is the first sub!"); }
} public class SourceSub2 implements Sourceable { @Override public void method() { System.out.println("this is the second sub!"); }
}
public abstract class Bridge { private Sourceable source; public void method(){ source.method(); } public Sourceable getSource() { return source; } public void setSource(Sourceable source) { this.source = source; }
}
public class MyBridge extends Bridge { public void method(){ getSource().method(); }
}
public class Client { public static void main(String[] args) { Bridge bridge = new MyBridge(); /*調用第一個對象*/ Sourceable source1 = new SourceSub1(); bridge.setSource(source1); bridge.method(); /*調用第二個對象*/ Sourceable source2 = new SourceSub2(); bridge.setSource(source2); bridge.method(); }
}
result:
this is the first sub!
this is the second sub!
6 組合模式 Composite Pattern
組合模式,又叫部分整體模式,用于把一組相似的對象當作一個單一的對象。 允許你將對象組合成樹形結構來表現 “整體/部分” 層次結構,組合能夠讓我們用一致的方式處理個別對象以及對象集合。
6.1 例子
- Component 抽象構件
- Composite 樹枝構件
- Leaf 樹葉構件
public abstract class Component {//個體和整體都具有的共享public void doSomething(){//編寫業務邏輯}
}
public class Composite extends Component {//構件容器private ArrayList<Component> components = new ArrayList<Component>()//增加一個葉子構件或樹枝構件public void add(Component component){this.components.add(component);}//刪除一個葉子構件或樹枝構件public void remove(Component component){this.components.remove(component);} //獲得分支下的所有葉子構件和樹枝構件public ArrayList<Component> getChildren(){return this.components;}
}
public class Leaf extends Component {/**
可以覆寫父類方法* public void doSomething(){* * }*/
}
public class Client {public static void main(String[] args) {//創建一個根節點Composite root = new Composite();root.doSomething();//創建一個樹枝構件Composite branch = new Composite();//創建一個葉子節點Leaf leaf = new Leaf();//建立整體root.add(branch);branch.add(leaf); }//通過遞歸遍歷樹public static void display(Composite root){for(Component c:root.getChildren()){if(c instanceof Leaf){ //葉子節點c.doSomething();}else{ //樹枝節點display((Composite)c);}}}
}
7 享元模式 Flyweight Pattern
用于減少創建對象的數量,以減少內存占用和提高性能。將多個對同一對象的訪問集中起來,不必為每個訪問者創建一個單獨的對象,以此來降低內存的消耗。
比如數據庫的連接池Pool。想想每個連接的特點,我們不難總結出:適用于作共享的一些個對象,他們有一些共有的屬性,url、driverClassName、username、password及dbname,這些屬性對于每個連接來說都是一樣的,所以就適合用享元模式來處理,建一個工廠類,將上述類似屬性作為內部數據,其它的作為外部數據,在方法調用時,當做參數傳進來,這樣就節省了空間,減少了實例的數量。
7.1 例子
- Shape 形狀
- Circle 圓
public interface Shape {void draw();
}
public class Circle implements Shape {private String color;private int x;private int y;private int radius;public Circle(String color){this.color = color; }public void setX(int x) {this.x = x;}public void setY(int y) {this.y = y;}public void setRadius(int radius) {this.radius = radius;}@Overridepublic void draw() {System.out.println("Circle: Draw() [Color : " + color +", x : " + x +", y :" + y +", radius :" + radius);}
}
public class ShapeFactory {private static final HashMap<String, Shape> circleMap = new HashMap<>();public static Shape getCircle(String color) {Circle circle = (Circle)circleMap.get(color);if(circle == null) {circle = new Circle(color);circleMap.put(color, circle);System.out.println("Creating circle of color : " + color);}return circle;}
}
public class Client {private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" };public static void main(String[] args) {for(int i=0; i < 20; ++i) {Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());circle.setX(getRandomX());circle.setY(getRandomY());circle.setRadius(100);circle.draw();}}private static String getRandomColor() {return colors[(int)(Math.random()*colors.length)];}private static int getRandomX() {return (int)(Math.random()*100 );}private static int getRandomY() {return (int)(Math.random()*100);}
}
result:
Creating circle of color : Black
Circle: Draw() [Color : Black, x : 36, y :71, radius :100
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 27, y :27, radius :100
Creating circle of color : White
Circle: Draw() [Color : White, x : 64, y :10, radius :100
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 15, y :44, radius :100
Circle: Draw() [Color : Green, x : 19, y :10, radius :100
Circle: Draw() [Color : Green, x : 94, y :32, radius :100
Circle: Draw() [Color : White, x : 69, y :98, radius :100
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 13, y :4, radius :100
Circle: Draw() [Color : Green, x : 21, y :21, radius :100
Circle: Draw() [Color : Blue, x : 55, y :86, radius :100
Circle: Draw() [Color : White, x : 90, y :70, radius :100
Circle: Draw() [Color : Green, x : 78, y :3, radius :100
Circle: Draw() [Color : Green, x : 64, y :89, radius :100
Circle: Draw() [Color : Blue, x : 3, y :91, radius :100
Circle: Draw() [Color : Blue, x : 62, y :82, radius :100
Circle: Draw() [Color : Green, x : 97, y :61, radius :100
Circle: Draw() [Color : Green, x : 86, y :12, radius :100
Circle: Draw() [Color : Green, x : 38, y :93, radius :100
Circle: Draw() [Color : Red, x : 76, y :82, radius :100
Circle: Draw() [Color : Blue, x : 95, y :82, radius :100