From fdbfa9e8ee9e7b6851a8ca871525203ee7c42306 Mon Sep 17 00:00:00 2001 From: hoswey Date: Sat, 14 Nov 2015 17:38:35 +0800 Subject: [PATCH] implements Twin design pattern #63 --- pom.xml | 1 + twin/.gitignore | 1 + twin/etc/twin.png | Bin 0 -> 29682 bytes twin/etc/twin.ucls | 79 ++++++++++++++++++ twin/index.md | 20 +++++ twin/pom.xml | 18 ++++ twin/src/main/java/com/iluwatar/twin/App.java | 53 ++++++++++++ .../main/java/com/iluwatar/twin/BallItem.java | 41 +++++++++ .../java/com/iluwatar/twin/BallThread.java | 53 ++++++++++++ .../main/java/com/iluwatar/twin/GameItem.java | 25 ++++++ .../test/java/com/iluwatar/twin/AppTest.java | 17 ++++ 11 files changed, 308 insertions(+) create mode 100644 twin/.gitignore create mode 100644 twin/etc/twin.png create mode 100644 twin/etc/twin.ucls create mode 100644 twin/index.md create mode 100644 twin/pom.xml create mode 100644 twin/src/main/java/com/iluwatar/twin/App.java create mode 100644 twin/src/main/java/com/iluwatar/twin/BallItem.java create mode 100644 twin/src/main/java/com/iluwatar/twin/BallThread.java create mode 100644 twin/src/main/java/com/iluwatar/twin/GameItem.java create mode 100644 twin/src/test/java/com/iluwatar/twin/AppTest.java diff --git a/pom.xml b/pom.xml index 3c86ed57e..4419fb98a 100644 --- a/pom.xml +++ b/pom.xml @@ -68,6 +68,7 @@ multiton resource-acquisition-is-initialization thread-pool + twin private-class-data object-pool dependency-injection diff --git a/twin/.gitignore b/twin/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/twin/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/twin/etc/twin.png b/twin/etc/twin.png new file mode 100644 index 0000000000000000000000000000000000000000..724092525bd6a90f6da664b1038c2493228fb5e5 GIT binary patch literal 29682 zcmce;bySsIw?1rulqg7-G>D{hE1-mQcc*l?3F+?c*dWqf(j_3B(nv|ern})==;L|c zGtT+WINvvZr5Ry`AWYa1R77Bcw;LNYD`ByzYrYk-#qq_S3l62GhddprNcw;ctz@c|HHtOzMN) zZqxiM@Qcha6cJ8vM*t?FfFcI|9_!zKN)c~zaeBK188V9E#z1^2I5>8GekjDrcon3m zW3-x=Cy$^|tTEo*{hW}!aN7TACz|_}xg zVTuG554c`9*45YZJKLizIPA*t@)0~Go`YftK?hT>kzzY(3<&ZtC?Hg53O+k|c4}&i zt-j=hMBA<|N%IYOIV4e+N>F`$YUMn+Vo~(ypOw%AFN8-I=F`{%aywHPcxb=q6C^H% zgoWW#dAYuI@;XM8y59=!s6*oD7e;cHt}mQaRWNDP$EnE3M429q39k?lZdsFYqS3x* zlA{)dNs#0sYRValE|ludzK{Frldn}@r71umtpzPZv2|K#<@QN$Ep;#BpfdUruZ$5_ zZZ@G*pcINB(ZNFV!Rh><$_uhIrdsM#q;AY=0XsO_XnB1zDfcyZL)(2jB2;&UQbTth!S*_I=7t7)3a`!bfWlKD^TyPOK zMq3IuHz!$bj^wTM=@a`Z1>-eb^sAejODcJJ{mcb@w43?cDqA@iol5K4AT_nby1s>G zU+Wi9Yis4MGV{k#=?9$fDTvIAnUkb&czfYL?2H#xrlnn89LsZc$CT+X zcZ3s%<1m<~B;mJrev6{ltb>!V(t^T^zj=VWn)_%Oi^@(xA=kXGJGwyDDaGsKxmP5& zz@MkjYL22rm8Ip!kLUX+*pF`){IgTOR~_$v7$6hKq>=By zN4|t9e(^Hkjb|j*EY`>qk7u`jwoxdTH3J)4;-Z-bTbz&(SfoDD+r{ZA`ZWB}klqxo z0zvGcEoX};l;qF8sz9;OjA*tbCu^~>-%WC0Jtd|Z&6A&+n!4||-IwTTWaN9!g{D2v z4&|=Y(S`4J^z%C@R>%vft)+463_mqKoNeGniMvOD7lo=?T~Q(2ZcQB#`s3^!M*N>O zm4+C(>@Ql!G5V(Q^X2wcxAjoyr}3`k`kd~D8=b$DVi&gh6VI+n#$O(Z{SIw8<1cD| ze)&KD>~{N2YdYGl|^@^Q?c?{k9iyKX4#CPbWwU$7^Q{jiIC-O3psC?@`S;4C&J zah3Li$DdPcSztFgM~SxbEwoK&spAz{-?bTMFH73Ui2Ut^S!cb2>aEe2CvG<`2TPqx zUbL(m9KVxJdK>KVhhmm~{foKodd6+h##EPs)mK*?scQY*%4?ew?mIs#V&-T3~ajm_Sn-P5PU=ElaFm8R#* z%PW_sjOL@cenl2GtK&Y=xa~wd?6j)G&qNlEIam&3eBYsKO!7FKz&*Mswf04>vavxw zD1YI^x&2QbQH0LoTsWvL5Zl=jnYvx?hc>;5}q7o<#c5&L=CFJJI;$O#` zuk~@vwah_HDU~EphO4rKdUjz@E0KmgLz^qrec4LHU1JmFgyrL8Obgb`jx$u-#qghO zuV_3uAZg_6-_bAJzuRz@bDkpAZ{z?skrp;>V^gx5q!aGjB-)l^HQ)3~D&id5>BvNz zpQaQ(Ded)Zxb2feJFB73+!AWvcc-g8qB$3Y6N*-zzIs=q-)V%{>;{c_?aptusRz;x zii2lCt*3X`*BVJxsFHr=)@9mA+2J5zRw{8ud#rcOG z$LalK+!rNC@11XvV_(~{LDu7tP@Xj>3^UH>9BW~Wet0)eTrIUWa=J5%P0eXMAS1Py zvD+TpM)KJBZ7R>H;iC-Gu>(Z%N46gf4a4@@{I#zNT7SMvIFCj}-45Mr_0`VK3?~ZK zi@I1#n?^7FO7}BY7Bv-v`s5uxBA-1GXKBFY=@KeCw1`B!%75f6mKv1hgGY7w138RP zTiofw-X6E5rEG0y7w^}>mmi+45vjrm_|Tjq{iL62b|48doI6d|J1mLEl)g~tdcntc z+?R;EwLL!{94+zYFuHwro|s-`sB2~>=KPt#2s+yPoq3u7o24t=F|u!$Fwl3j1!5G; zHHx2~?bnpqkZygtIO^5#$yvi$f;L9S#J@{y`*77mEB5zndXFsOa7o)bXb&}7ZAFOf zFR(oAYh!Gk%|NId*z3^R_+A>*8*_e8JUBW!*;^1;V8(VbX1D!QDrNZ)$p_Rw96DUB z#!v7*$!rC_j@duy9%D0FYJGm=Wac&@A^~D%r5P7M_@TN#bj3&O3L9Z(sNHGY? z5Lb{1=4vWhy;!K8NJ85{LRD?WY_!o|1O1wTL(3A%*|^7us)_s)_Q}u+~-?zKInklOm4h;g!)6T%bfm4->wlvpx*xH7`0-39|V^?q4xxUQk zPo7w%2u9;`cS97()2O9W$Df~_Jzi+@v7A+NSpTI|5J%qgmO{PAeYTE${DL7jhUj*0s zQJ!;uWudl_Q_PVvlq%pd47mBhMt z2uR9kGMHf*XJ@Rh9T@C8!&FlY*sk*xn`63@+sfq4FBw2;XsDSrI_>RbIv_~&HB|+>+1_e5KyRz1YG3^>=+pF$J>|f(mn0Ln^Srx zDJ~GVOLpHpmMxpf(xr~`+p9x5Pzun|1!SfXo<6&}o_2C#+n5?@YiS9Kex@;{DVk>Z zJW-lXg|lR!@MmIT&Y7YxBU|#QGf?_Gz~ub!p(~oxjSHQ;vDBA?{-m*22V%y#&#&GIL+Ms!&pH*cL8Z zV&it9txO}H#nvGFa7}-{`88~1Yg-0^^0iJh16uGtIXs)4Wc$mA>yH z)vN6TIe#T`8ylp&QA{tYrCsic?Qc-u=}&IMW*|(rAsrF_15HyBQB^+sq7d93PdwQ$ za6TQVcjkERIXT&_m>)wc_dV0zA_pK)&OkF$_gi}*zmMhR%%^)M)=M1&tNmuvQ>u(A zOergpVyX*UTlZW~_dnb$ci+eE9<3FRkIT)Ej^>z7OEv+!gfHb>wbf-LxpV241ny=>8(c-# zJ(7hV8Ls8snDHTSU1Syx)#j?yY;5nB)Uw_G+=Lu#Mz<}Oe_G`BVESinbTHbtYy~9k zXWu&qi&V;3QJS5iHL5LRxgjsTD~z9tiHkSb8iJIRTW7rxkiC7B=lf& zs3p`_7hPXjLBXWWZ!8kshwn*!ad+hGB6A`f+nkmNiWB7)eND7t3ZW=E<8;Nx~592sWJN>Q4>*D1y0BsN^*c z_LqJ%jI*>ZrZT06cT`H;{%+?2w#KGR)TT?nm+ocml}J44q#~=Js3IhQY&N^T?Mf|s7S_VZj-E)VD%k46Qqh{s zE-5W8?t|W_-$}>Av$?-?4LXdQwx^Pj_l7r?MrMBeNbQw^&Nqs`;^1hs)+N&We8y&` zS<9ASrl;G*0Fg5QbvsOm0Hdb#!} z>O4kwQ?AgPB^*tx))cGp!f_~%Gn&1fovyY4#=D&2r;oS&$MY*rt~}pN@T8L#%Q~DN z_$_s2r15fYW_)lxY9yWpf7kT$1qm7bJ8eLjPp;~m|aM-wob zECr600t&=c$K-V;!d4hjg-#uVDs$8|%bVY<82p5qo?-)b%)-AmM>*FD-G~M z^X~Horlx&2BfSB8w>Qu+d_7LMGiR<%JLya5-+|q7-+4<#q2509eN5@E+qPKBf0!%I?$Vbk zL+gN3^fIIOh*$+J$}tRJErmRod6eRK|U`JV@F zuPT%!*(iE4?(RioM+>us&bJY1mvBL$%$&o94UGg6#?ed^z=qr#h3b) zmO=9%4Nkdmlni)LeiQaH5E|d!DwKUSgJhupxC&!c5ZWwd#&i%(FeLL8b>}OH-_?E; zzkJ&$q?^)GkLP?pNAp(@W8XfYrgmnL$SDh^SX7c!EXS@r~>`XnqZ{V`e+K1#NON zFYb@l&gTbRi|wQx9lii!kTX+!pJ-{Z+!&UNkwlbSZYyn$xHM{` zF?bE70qL$=>3M+r1U!nK?@R+8g5h_q7nKufgTLaW1Sc@dRnM~reE%2 z0U@tq&Oy+|B}1d3rb!CgL-%+D_uZ>l5I9>G$7!lpnPS&E%tBs#y!d-alb=#Sl89%t z@;HB-wYtVVGoz6SKMME&C(#8i&?Y(^dXd(EI0i%toFpg#*v-}I)#C&lQh;`qttovK z_OCtrQ#Ux9WKVXWo%J-Tbsu9IUB%#TZg`&5byIt6l_TYOBj8lPr7Tyrfi_^?{kFY~ zV-(M;*ZI7nqQXz5Vh>a(-EE`9;-Da8)F(UKA5GTI5B+uRt}pV2T&qxMRP84>3ZHm^ zr4IkS)R_aH=8@X>Ll3UbN!6zpOSCAP+}*#rUFQSRPH4GU)6OxcAeqCz(qbCT^XOHQFM$d%Wqp#Le`vJ%7qh}iOr(vUZl1CtqcXe7?HVCJ4@r=;C3BFLPBuq4p)^^vF zuPNQnk?!Q`-V_)Glxmqv+=r1zKPOe#OcmN@XJfmM0E~;KI*wKr`f`!G`B3Om09JSk zm64c#Q7Vyj+e0JXp7zs#6f>pYmV%nmSf02Ct%J=DHMJi9S^Mwc@DI9UW%K4 z9ijSfYb;d|zKc`032g(cW!No2h^^!x(pqMX+2WgW)P!NWiw}`bC7i6WQ8P8gyc9x~ zt^R$pk(>8Xkp5eWna^UE)xWGi6TVIeq1r`|)7F5-Z1EW--RNv&R8-v3o2|;|bco=# zqVX3g`MV53?(p#BC`-OHsk76UN|Prg>l?ZI^OPFn2XrQGK})|{+6$D!5efjXZ-Ym0 zFibZ804HY*@&1nnVcGKA#lMJT!6(|Y&72?R=H~6mT`t>;rVxzub^-IJYFk@hzi17B zOz-e_nKdFFBgH(qfuD6MV2PYHZx`DaK*tUb4|#&PC+s58_gy6X1hO+TJY1d&&UB%w z(3!*QwSo4Fi>k7+vhr%ijqTYO?fl308B3@@J;Z0rFZ;OhLP%x}0Fho{fS|?%iSTi` zgJw`wPJM5nN@txZ8vf6UUpM{WFhbMcTSfPtK=9=Ia!D#Vu3(xx3|E)xjH}eLBb#; zBBE8ul_}P!1xy{E^GS#_m}}fkEyqGMrRxFtV9DK1RLYZ^m*TEhKsXhpf?+0xP)S>Mg#-p#cvIFh9` zEX-2hzK4Z9uZCZpf>&C{$QfGLHYQ^eEj`Wn)L8#!L8`PNoOEV)mXkCrCNaYw+@OW)XcW&3zOgKoE zb_!~*SgZ;9hD^S8bl6D=m8?SKkvCQup_@wK5-OYeEN~2vpohKv_@qGrb7mH$>XmV2&2Y+$iQ!Z~+y zM@RH-WAgqpjqOWVo5cOG*9SgOTVf-AcjQwE671`j-Ej3B%Cz}2(qIis|o-uAT%Z~kju)VBj@IuiF%t1 z#Z{gc90ubsM(nqEBY@`=CgJ5JT$pQgcGx;IY_FEjk;22nlZ0|ZcIO(yiC_CQSWcEM z++1I}UY-zf*-|{ysJBP0b6IHh1t$Xm4z8xYp8TLAl%PErXTH*G!m9cD5t}jWaicv1 z(r1O8|>1HK{T`0dI52+u{bIB46Uyp8AarsFT1INcVoZF&(W2^ z*A<`Wlp?P$okI!MN_td#?GW_!^3Mq{4s6+Zz6&MADC7|VKN`HaH*dkeE% z-M6D?Mldic^<~;sWM>)@@n#_~GBToLrlCQKW2BZf4qyxp2@3McOQvekcE5&q#*0MM zuC;}~kK2xd!%(PNK_xy_sLB$D5-9@NgPX z8HXAZFu;x+%=`?N0;!RFf-xw&lhHPHd8g^K^ZX=f^kSRw^8|lwpL&s}AE67OjpeGg z=>trzlMFAsV`FQ1)|khDdUYHxKqp zgBS!Dul28Qg3m}uiUy>lr0DLO>mnc^NPQW&GC0Z+v0g`&uad7J;vWK)K!87LY)YJfj8Ge#?V%P_huM;AF)EY* zI+?LYsQKh_7ieCTdzMO4DJqKPnOR>$Z$S>4>6mYlj69xuaC3E7m@dSFfZ*^>gpjMe zbETI6Hdf%Je|^yF;sg=$gh#=rq%M(h84nVVgCKZQx9~ZWZBCT@Y;x77Xlo;(6&WHA z(OFJn!{BzXq$&C7mBTollsTaGKv8Q+$V-!|-a+k0^K`!F>=%SZ9Nq7T?<;~}k5l;@ z3m`9r3}`p~yZm_rHCN>^1@qD%Ud@T*l)ym51COLGa zH$LO*>zlKs-R9e3?PfP$WUM3(s~c?$+L6hr3*m(uh(N3B=2LhjTy^6o!x#iSc!L|I z2&66A!P~z&Zl1k_!m|_gwcTD@(Na8QrcUtfw>i}E*PU-sFz}C{_q-hMM-CCmmPz^k zR2&e10w>6VL(V9L;;T!@uJHIhbF>UTH#$=T`M7mVYN&Y)b3nL0C= z68MUp0j@rT+F2>eKKr_42`d8sT z&%QwR(K_7<^naqeOV1=bwLwAAx7Jv|R2%QW4br)<-^l<7k(j>L@*N%P@vCM2j~Fnz zb#!5-XKTSZqLYjVFc2VKMS0_%0y57s2TZqptmYbInJKpnZ%_=M2LNtq#qBSWJDN=pvX7w@Yx0xm zUScCzjg3C_7UQIMX{GMmBLs()CP=z1;2FFhjZGEgXf^&67QT6rurgbZNlSYOnld07 zNvA8@hwkxYzh7V8qiMI) zl_`5oW}W?WYy-p7P#QZJ$#jz|ej=N^gH%Pdet!N8P25GR3#I@$M~U_7ZinCy4k zB2GQaoGr9|mR?rH;CB>J3i@48{spn&8>@e1PNvDXypQd9b5U#6EFN7%K<8kDNz1=! z{yS|Kz_m5oTTmx_bNuMI_iHn0qWrlo9Jy|4#WCtX%lm=k-|-eQ;<C>kZqHQ!!w~s@~$P$yWLY0NYVE#O~zd?*@ zqLky>093y-?47Xi#o1Zu>ktwT$+vD7M`9xu38;_tLb+Fs-Yt#EL|rUA@dK?4n(H^q zsb?KcY&~oo<_X+3dfK6c|G=H}*ldc(Q0BwZH%A)N@#G(x4~oxb3I`8A+vLhaMb$AeQ4RP`#e4-kcJoQb<|{CT5Rt6T z)c9wn1`(2EAwXLMP)+v`upI?T^n+sKLpt+I;kZAp*V2r;seW~jsJsVt{GQsd0@W}C!-=gy>%pc^kT(W|T zVO|=2{O0jwi#VV7^>btQC^-k}zOF9B$B%u`QRUTEnN4ZBIFM!OM^~jvh-IR=g@kb! zpDZ+8JFLghTst3`wB4waBzQ#rxnM<%6QrYT^7O_@pXX4eRL8TZX#4u|w7D@v4Y}_E_0(P4|kk#A5J_I;uehLqcdR1X)gP>9&ZX34OFA6uCAk0?_ z@fX5n06~+m7D~+VAQkG<(%vs2TW0Z<4ld^a014%8G8jDp-N&Kl2qe zaph3)71rXPVnAW?YoON*2?^0K=tF;d7z#>sp-=As9|G|&X#N|7eH}#rhyh4HT!j~6 zRmE0id=3Ulow6!5K-gqZT}eN{{$ox?5pUtd&IwAt-?IqNk5zRAhSSy@?Cxpq}evIfxDnMQ(=Z+s$g-_nJGN#R7@ z*9M+T9b|EjI^Ev*9yB--@?f{8e%HO%+Tgn%(9`DP?N;{G!BK?1p$&5#MZ#I>iTSr^ zYGpeRi%@=f07p^f*39!7xB+T!RxD7WTV3r>#>U1z7zU64D7^Syybz@wRmGcsQ9ke< zvwrQ@!v=Y*gvJJUv`Ldb4u4+8=S=f^Z&0^AToJ|`FWKQYfT;SN?CI_;*>`#ymEB79e!3^6 zNCIc^v!?t=JU&dXiEEjQxFw?+=oG6g2*_3@-s59=KzTlz7;oH#wG6{tCxYd}E0HAd zT&{nfi&q&P^I`mp>D~nCVOit;BsP;F671;2#6Zz@`N5xG271nfBOyv(CjZ14Nfr

{=^~r1=1stT~bBS&d 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twin/index.md b/twin/index.md new file mode 100644 index 000000000..241c31f58 --- /dev/null +++ b/twin/index.md @@ -0,0 +1,20 @@ +--- +layout: pattern +title: Twin +folder: twin +permalink: /patterns/twin/ +categories: Creational +tags: Java +--- + +**Intent:** Twin pattern is a design pattern which provides a standard solution to simulate multiple +inheritance in java + + + +![alt text](./etc/twin.png "Twin") + +**Applicability:** Use the Twin idiom when + +* to simulate multiple inheritance in a language that does not support this feature. +* to avoid certain problems of multiple inheritance such as name clashes. \ No newline at end of file diff --git a/twin/pom.xml b/twin/pom.xml new file mode 100644 index 000000000..c2a4b131b --- /dev/null +++ b/twin/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.8.0-SNAPSHOT + + twin + + + junit + junit + test + + + diff --git a/twin/src/main/java/com/iluwatar/twin/App.java b/twin/src/main/java/com/iluwatar/twin/App.java new file mode 100644 index 000000000..4ecba711f --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/App.java @@ -0,0 +1,53 @@ +package com.iluwatar.twin; + +/** + * Twin pattern is a design pattern which provides a standard solution to simulate multiple + * inheritance in java. + * + *

+ * In this example, there is a ball game, a ball needs to subclass {@link GameItem} which provide + * some common method like draw and click. Moreover, it needs to subclass {@link Thread} as ball is + * a moving item (we use {@link Thread} instead of implements {@link Runnable} for example only) + *

+ * Threre is scenario, when user click the ball, the ball will stop, when user click it gain, it + * will resume to move. We create two class, ons is {@link BallItem} which subclass {@link GameItem} + * , another is {@link BallThread} which subclass {@link Thread}. These two object both hold a field + * named "Twin" reference to another object. In {@link BallItem#click()}, it will invoke + * {@link BallThread} to suspend or resume moving; in {@link BallThread#run()}, it will invoke + * {@link BallItem} for drawing. + * + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) throws Exception { + + BallItem ballItem = new BallItem(); + BallThread ballThread = new BallThread(); + + ballItem.setTwin(ballThread); + ballThread.setTwin(ballItem); + + ballThread.start(); + + waiting(); + + ballItem.click(); + + waiting(); + + ballItem.click(); + + waiting(); + + ballThread.stopMe(); + } + + private static void waiting() throws Exception { + Thread.sleep(2500); + } +} diff --git a/twin/src/main/java/com/iluwatar/twin/BallItem.java b/twin/src/main/java/com/iluwatar/twin/BallItem.java new file mode 100644 index 000000000..b95dc06ee --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/BallItem.java @@ -0,0 +1,41 @@ + +package com.iluwatar.twin; + +/** + * This class represents a Ball which extends {@link GameItem} and implements the logic for ball + * item, like move and draw. It hold a reference of {@link BallThread} to delegate the suspend and + * resume task. + */ +public class BallItem extends GameItem { + + private boolean isSuspended = false; + + private BallThread twin; + + public void setTwin(BallThread twin) { + this.twin = twin; + } + + @Override + public void doDraw() { + + System.out.println("doDraw"); + } + + public void move() { + System.out.println("move"); + } + + @Override + public void click() { + + isSuspended = !isSuspended; + + if (isSuspended) { + twin.suspendMe(); + } else { + twin.resumeMe(); + } + } +} + diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java new file mode 100644 index 000000000..dae537e64 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -0,0 +1,53 @@ + +package com.iluwatar.twin; + +/** + * This class is a UI thread for drawing the {@link BallItem}, and provide the method for suspend + * and resume. It hold the reference of {@link BallItem} to delegate the draw task. + * + */ + +public class BallThread extends Thread { + + private BallItem twin; + + private volatile boolean isSuspended; + + private volatile boolean isRunning = true; + + public void setTwin(BallItem twin) { + this.twin = twin; + } + + public void run() { + + while (isRunning) { + while (!isSuspended) { + twin.draw(); + twin.move(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + + } + } + } + + public void suspendMe() { + isSuspended = true; + System.out.println("Begin to suspend BallThread"); + } + + public void resumeMe() { + isSuspended = false; + System.out.println("Begin to resume BallThread"); + } + + public void stopMe() { + this.isRunning = false; + this.isSuspended = true; + } +} + diff --git a/twin/src/main/java/com/iluwatar/twin/GameItem.java b/twin/src/main/java/com/iluwatar/twin/GameItem.java new file mode 100644 index 000000000..d797eda95 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/GameItem.java @@ -0,0 +1,25 @@ + + +package com.iluwatar.twin; + +/** + * GameItem is a common class which provides some common methods for game object. + */ +public abstract class GameItem { + + /** + * Template method, do some common logic before draw + * + * @param other + * @return + */ + public void draw() { + System.out.println("draw"); + doDraw(); + } + + public abstract void doDraw(); + + + public abstract void click(); +} diff --git a/twin/src/test/java/com/iluwatar/twin/AppTest.java b/twin/src/test/java/com/iluwatar/twin/AppTest.java new file mode 100644 index 000000000..94e178254 --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/AppTest.java @@ -0,0 +1,17 @@ +package com.iluwatar.twin; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +}