端午腦波一弱買個 Android 的課來看看 , 結果一開始教 design pattern XD , 只好硬著頭皮跟著學 範例是用 java , 自己用 c# 改寫下
改寫 observer pattern 之前 動物的部分 這裡幾個問題點 , java 用 ImageIcon , 我 c# 想說用 Icon 可是 Icon 好像要真的是符合 ico 檔的大小才可以 所以用 Bitmap
就沒這個問題
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public interface Animal { void Draw(Graphics g); void Dance(); } public class RedPanda : Animal { private Point point; private Bitmap redPanda = new Bitmap(@"Images\redpanda.png"); public RedPanda(int x, int y) { point = new Point(x, y); } public virtual void Draw(Graphics g) { g.DrawImage(redPanda, point.X, point.Y); } public virtual void Dance() { Random random = new Random(); int newX = (int)(random.NextDouble() * 10 - 5); int newY = (int)(random.NextDouble() * 10 - 5); point.X += newX; point.Y += newY; } } public class Dog : Animal { private Point point; private Bitmap dog = new Bitmap(@"Images\dog.png"); public Dog(int x, int y) { point = new Point(x, y); } public virtual void Draw(Graphics g) { g.DrawImage(dog, point.X, point.Y); } public virtual void Dance() { Random random = new Random(); int newX = (int)(random.NextDouble() * 10 - 5); int newY = (int)(random.NextDouble() * 10 - 5); point.X += newX; point.Y += newY; } }
winform 的部分 java 裡用的 scheduleAtFixedRate
在 c# 裡面比較相似的應該是 System.Threading.Timerhttps://learn.microsoft.com/zh-tw/dotnet/api/system.threading.timer.-ctor?view=net-7.0#system-threading-timer-ctor(system-threading-timercallback-system-object-system-int32-system-int32) 他吃 4 個參數
TimerCallback 委派 => 這個要寫個參數為 (object state) 的方法
state => 這裡可以傳任何物件 , 一般會傳想要的資訊進去
dueTime => 延遲時間
period => 幾毫秒呼叫一次
他的範例用到 inner class , 我自己實戰沒怎用過 inner class , 而且 c# 的 inner class 好像沒辦法跟 java 一樣直接可以訪問 parent 所以在 TimerMission 的 Run 方法直接把 state 轉型為 parent , 或是直接在建構子傳入應該也可以 這裡只要呼叫 Invalidate
就會觸發 OnPaint
讓表單重新繪製
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public class TimerMission { private List<Animal> animals; public TimerMission(List<Animal> animals) { this.animals = animals; } public void Run(object state) { foreach (var animal in animals) animal.Dance(); //把 parent 送進來並且強制轉型 var parent = (Form1)state; //呼叫這句可以觸發 OnPaint parent.Invalidate(); } } public partial class Form1 : Form { private List<Animal> redPandas = new List<Animal>(); private List<Animal> dogs = new List<Animal>(); private System.Threading.Timer timer; private int speed = 100; protected override void OnPaint(PaintEventArgs e) { e.Graphics.Clear(Color.White); foreach (var redPanda in redPandas) { redPanda.Draw(e.Graphics); } foreach (var dog in dogs) { dog.Draw(e.Graphics); } } public Form1() { InitializeComponent(); this.StartPosition = FormStartPosition.CenterScreen; this.Width = 500; this.Height = 500; } protected override void OnLoad(EventArgs e) { timer = new System.Threading.Timer(new TimerMission(this.redPandas).Run, this, 0, speed); timer = new System.Threading.Timer(new TimerMission(this.dogs).Run, this, 150, speed += 500); redPandas.Add(new RedPanda(30, 30)); redPandas.Add(new RedPanda(230, 230)); redPandas.Add(new RedPanda(90, 100)); dogs.Add(new Dog(200, 100)); dogs.Add(new Dog(300, 50)); dogs.Add(new Dog(400, 400)); } }
套用 observer pattern 之後 動物部分 新增 interface TickListener
, 並且註解 Animal
, 改繼承 TickListener
實際上沒把 Animal
註解起來應該也可以 , 只不過在加入 Register
時要強制轉型為 RedPanda
or Dog
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 //Observer public interface TickListener { //notify public void Tick(); } //public interface Animal //{ // void Draw(Graphics g); // void Dance(); //} public class RedPanda : TickListener { private Point point; private Bitmap redPanda = new Bitmap(@"Images\redpanda.png"); public RedPanda(int x, int y) { point = new Point(x, y); } public virtual void Draw(Graphics g) { g.DrawImage(redPanda, point.X, point.Y); } public virtual void Dance() { Random random = new Random(); int newX = (int)(random.NextDouble() * 10 - 5); int newY = (int)(random.NextDouble() * 10 - 5); point.X += newX; point.Y += newY; } public void Tick() { this.Dance(); } } public class Dog : TickListener { private Point point; private Bitmap dog = new Bitmap(@"Images\dog.png"); public Dog(int x, int y) { point = new Point(x, y); } public virtual void Draw(Graphics g) { g.DrawImage(dog, point.X, point.Y); } public virtual void Dance() { Random random = new Random(); int newX = (int)(random.NextDouble() * 10 - 5); int newY = (int)(random.NextDouble() * 10 - 5); point.X += newX; point.Y += newY; } public void Tick() { this.Dance(); } }
subject 部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 //subject public class TimerMission { private List<TickListener> listeners = new List<TickListener>(); //add observer public void Register(TickListener listener) { this.listeners.Add(listener); } //remove observer public void Unregister(TickListener listener) { this.listeners.Remove(listener); } //notify observers public void Run(object state) { foreach (var listener in listeners) { listener.Tick(); } } }
winform 部分
也讓表單繼承 TickListener
然後實作 Tick
讓他重新繪製
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public partial class Form1 : Form, TickListener { private List<RedPanda> redPandas = new List<RedPanda>(); private List<Dog> dogs = new List<Dog>(); private System.Threading.Timer timer; private int speed = 100; private TimerMission tm1 = new TimerMission(); private TimerMission tm2 = new TimerMission(); protected override void OnPaint(PaintEventArgs e) { e.Graphics.Clear(Color.White); foreach (var redPanda in redPandas) { redPanda.Draw(e.Graphics); } foreach (var dog in dogs) { dog.Draw(e.Graphics); } } public Form1() { InitializeComponent(); this.StartPosition = FormStartPosition.CenterScreen; this.Width = 500; this.Height = 500; } protected override void OnLoad(EventArgs e) { timer = new System.Threading.Timer(tm1.Run, this, 0, speed); timer = new System.Threading.Timer(tm2.Run, this, 150, speed += 500); redPandas.Add(new RedPanda(30, 30)); redPandas.Add(new RedPanda(230, 230)); redPandas.Add(new RedPanda(90, 100)); foreach (var redPanda in this.redPandas) { tm1.Register(redPanda); } tm1.Register(this); dogs.Add(new Dog(200, 100)); dogs.Add(new Dog(300, 50)); dogs.Add(new Dog(400, 400)); foreach (var dog in dogs) { tm2.Register(dog); } tm2.Register(this); } public void Tick() { this.Invalidate(); } }