面向對象23種設計模式系列(三)- 行為型設計模式

来源:https://www.cnblogs.com/xyh9039/archive/2020/07/27/13340315.html
-Advertisement-
Play Games

本系列將和大家分享面向對象23種設計模式中常用的幾種設計模式,本章主要簡單介紹下行為型設計模式。 ...


行為型設計模式:關註對象和行為的分離。(共11個)

  甩鍋大法:把鍋丟出去,只管自己,哪管洪水滔天。把不穩定的地方移出去,自己只寫穩定的,能保證自身的穩定。

  沒有什麼設計模式是完美無缺的,一個設計模式就是解決一類的問題的,通常設計模式在解決一類問題的同時,還會帶來別的問題,我們設計者要做的事兒,就是要揚長避短,充分發揮長處!

  很多時候,可能會融合應用多個設計模式,分別解決對應的問題。

下麵我們結合幾種具體場景來分析幾種行為型設計模式。

1、模板方法模式(TemplateMethod Pattern)

模板方法設計模式在基類父類定義流程,把可變邏輯分離到不同子類實現

有個複雜的多步驟業務,定義一個父類(模板),模板負責完成流程,把步驟分解,固定不變的當前類--各不相同的子類--有的相同有的不同虛方法。

就是把部分行為做了分離。

好處:就是可以擴展,職責分明。

場景:銀行客戶端查詢餘額業務,它通常是包含幾個步驟,例如:第一是校驗用戶名和密碼,第二是查詢真實餘額,第三是計算利息,第四將最終信息展示給客戶。對於不同的客戶而言,這些步驟流程一般是固定不變的,但每一步的具體實現又有可能是不一樣的。比如普通客戶和VIP客戶計算利息的利率是不一樣的、活期和定期的利率也是不一樣的,又比如普通客戶和VIP客戶的信息是存在不同的資料庫而導致用戶名和密碼校驗的具體實現可能也是不同的。這時候就可以考慮使用模板方法設計模式了,在基類中定義流程,把可變邏輯分離到不同子類實現。

下麵我們來重點看下代碼:

 抽象銀行客戶端:

using System;

namespace TemplateMethodPattern
{
    /// <summary>
    /// 抽象銀行客戶端
    /// </summary>
    public abstract class AbstractClient
    {
        /// <summary>
        /// 查詢(模板方法)
        /// </summary>
        public void Query(int id, string name, string password)
        {
            if (this.CheckUser(id, password))
            {
                double balance = this.QueryBalance(id);
                double interest = this.CalculateInterest(balance);
                this.Show(name, balance, interest);
            }
            else
            {
                Console.WriteLine("賬戶密碼錯誤");
            }
        }

        /// <summary>
        /// 用戶名和密碼校驗
        /// </summary>
        public virtual bool CheckUser(int id, string password)
        {
            return DateTime.Now < DateTime.Now.AddDays(1);
        }

        /// <summary>
        /// 查詢真實餘額
        /// </summary>
        public virtual double QueryBalance(int id)
        {
            return new Random().Next(10000, 1000000);
        }

        /// <summary>
        /// 計算利息
        /// 活期/定期/VIP 利率不同
        /// </summary>
        public abstract double CalculateInterest(double balance);

        /// <summary>
        /// 信息展示
        /// 有些一樣,有些不一樣
        /// </summary>
        public virtual void Show(string name, double balance, double interest)
        {
            Console.WriteLine("尊敬的{0}客戶,你的賬戶餘額為:{1},利息為{2}",
                name, balance, interest);
        }
    }
}

活期用戶:

using System;

namespace TemplateMethodPattern
{
    /// <summary>
    /// 銀行客戶端(活期用戶)
    /// </summary>
    public class ClientCurrent: AbstractClient
    {
        /// <summary>
        /// 活期利率不同
        /// </summary>
        public override double CalculateInterest(double balance)
        {
            return balance * 0.001;
        }
    }
}

定期用戶:

using System;

namespace TemplateMethodPattern
{
    /// <summary>
    /// 銀行客戶端(定期用戶)
    /// </summary>
    public class ClientRegular: AbstractClient
    {
        /// <summary>
        /// 定期利率不同
        /// </summary>
        public override double CalculateInterest(double balance)
        {
            return balance * 0.003;
        }
    }
}

VIP用戶:

using System;

namespace TemplateMethodPattern
{
    /// <summary>
    /// 銀行客戶端(VIP)
    /// </summary>
    public class ClientVip : AbstractClient
    {
        /// <summary>
        /// 計算利息
        /// VIP利率不同
        /// </summary>
        public override double CalculateInterest(double balance)
        {
            return balance * 0.005;
        }

        /// <summary>
        /// VIP的Show不一樣
        /// </summary>
        public override void Show(string name, double balance, double interest)
        {
            Console.WriteLine("尊貴的{0} vip客戶,您的賬戶餘額為:{1},利息為{2}",
                name, balance, interest);
        }
    }
}

使用如下:

using System;

namespace TemplateMethodPattern
{
    /// <summary>
    /// 模板方法設計模式
    /// 
    /// 有個複雜的多步驟業務,定義一個父類(模板),模板負責完成流程,把步驟分解,固定不變的當前類--各不相同的子類--有的相同有的不同虛方法。
    /// 就是把部分行為做了分離。
    /// 好處:就是可以擴展,職責分明。
    /// 設計模式沒那麼神奇,只不過是把常用的東西跟場景結合,沉澱下來起個名字。
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                {
                    AbstractClient client = new ClientCurrent();
                    client.Query(101, "張三", "123456");
                }
                {
                    AbstractClient client = new ClientRegular();
                    client.Query(102, "李四", "000000");
                }
                {
                    AbstractClient client = new ClientVip();
                    client.Query(103, "王五", "251146");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.ReadKey();
        }
    }
}

運行結果:

2、觀察者模式(Observer Pattern)

觀察者模式:一個對象動作觸發多個對象的行為(動作),通過觀察者可以去掉對象的依賴,支持各種自定義和擴展。

主題(Subject)發生變化是導致觀察者(Observer)觸發動作的根本原因。觀察者是觀察主題的。

場景:貓(主題)叫了一聲,觸發了觀察者的一系列動作,例如:老鼠跑、狗吼、雞叫、孩子哭等等。

下麵我們來重點看下代碼:

首先我們來看下不是使用觀察者模式實現的普通方法

using System;
using System.Collections.Generic;

using ObserverPattern.Observer;

namespace ObserverPattern.Subject
{
    /// <summary>
    /// 一隻神奇的貓
    /// 
    /// 貓叫一聲之後觸發一系列動作
    /// 老鼠跑、狗吼、雞叫、孩子哭
    /// </summary>
    public class Cat
    {
        /// <summary>
        /// 貓Miao了一聲
        /// 
        /// 依賴太多,任何一個對象改動,都會導致Cat變化。
        /// 違背了單一職責,不僅自己miao,還要觸發各種動作,不穩定。
        /// 加一個/減一個/調整順序  Cat都得改
        /// Cat職責:
        ///     1、Miao  2、觸發一系列動作  這是需求
        ///     實現上,這裡多了一個,3、指定動作
        /// </summary>
        public void Miao()
        {
            Console.WriteLine("{0} Miao.....", this.GetType().Name);

            //依賴了
            new Mouse().Run(); //老鼠跑
            new Dog().Wang(); //狗吼
            new Chicken().Woo(); //雞叫
            new Baby().Cry(); //孩子哭
        }
    }
}

上面的這段代碼就很不穩定,依賴太多,任何一個對象改動,都會導致Cat變化。違背了單一職責,不僅自己miao,還要觸發各種動作,不穩定。加一個/減一個/調整順序  Cat都得改。

竟然Cat不穩定,那我們就可以考慮將這一堆對象甩出去,自己不寫讓別人傳遞。

接下來我們使用觀察者模式實現下:

首先定義一個介面,這個介面只是為了能夠使多個對象產生關係,方便保存和調用

using System;

namespace ObserverPattern
{
    /// <summary>
    /// 只是為了使多個對象產生關係,方便保存和調用
    /// 方法本身其實沒用
    /// </summary>
    public interface IObserver
    {
        void Action();
    }
}

讓我們的觀察者都去實現這個介面

using System;

namespace ObserverPattern.Observer
{
    /// <summary>
    /// 老鼠
    /// </summary>
    public class Mouse : IObserver
    {
        public void Action()
        {
            this.Run();
        }

        public void Run()
        {
            Console.WriteLine("{0} Run", this.GetType().Name);
        }
    }
}
using System;

namespace ObserverPattern.Observer
{
    /// <summary>
    ////// </summary>
    public class Dog : IObserver
    {
        public void Action()
        {
            this.Wang();
        }

        public void Wang()
        {
            Console.WriteLine("{0} Wang", this.GetType().Name);
        }
    }
}
using System;

namespace ObserverPattern.Observer
{
    /// <summary>
    ////// </summary>
    public class Chicken : IObserver
    {
        public void Action()
        {
            this.Woo();
        }

        public void Woo()
        {
            Console.WriteLine("{0} Woo", this.GetType().Name);
        }
    }
}
using System;

namespace ObserverPattern.Observer
{
    /// <summary>
    /// 小孩
    /// </summary>
    public class Baby : IObserver
    {
        public void Action()
        {
            this.Cry();
        }

        public void Cry()
        {
            Console.WriteLine("{0} Cry", this.GetType().Name);
        }
    }
}

我們的貓(主題)添加一個觀察者對象容器,讓外部去添加對象

using System;
using System.Collections.Generic;

namespace ObserverPattern.Subject
{
    /// <summary>
    /// 一隻神奇的貓
    /// 
    /// 貓叫一聲之後觸發一系列動作
    /// 老鼠跑、狗吼、雞叫、孩子哭
    /// </summary>
    public class Cat
    {
        /// <summary>
        /// 存放觀察者的容器
        /// </summary>
        private List<IObserver> _observerList = new List<IObserver>();

        /// <summary>
        /// 添加觀察者
        /// </summary>
        public void AddObserver(IObserver observer)
        {
            this._observerList.Add(observer);
        }

        /// <summary>
        /// 觀察者模式Miao
        /// </summary>
        public void MiaoObserver()
        {
            Console.WriteLine("{0} MiaoObserver.....", this.GetType().Name);
            if (this._observerList != null && this._observerList.Count > 0)
            {
                foreach (var item in this._observerList)
                {
                    item.Action();
                }
            }
        }
    }
}

使用如下:

using System;

using ObserverPattern.Observer;
using ObserverPattern.Subject;

namespace ObserverPattern
{
    /// <summary>
    /// 觀察者模式
    /// 對象和行為的分離
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                {
                    Console.WriteLine("***************Observer***************");
                    Cat cat = new Cat();
                    cat.AddObserver(new Mouse());
                    cat.AddObserver(new Chicken());
                    cat.AddObserver(new Baby());
                    cat.AddObserver(new Dog());
                    cat.AddObserver(new Mouse());
                    cat.MiaoObserver();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.ReadKey();
        }
    }
}

運行結果:

上面的這種是標準的觀察者模式,是使用面向對象的方式實現的。其實觀察者模式還有一種更優雅的寫法,就是使用事件來實現的。如下所示:

using System;

namespace ObserverPattern.Subject
{
    /// <summary>
    /// 一隻神奇的貓
    /// 
    /// 貓叫一聲之後觸發一系列動作
    /// 老鼠跑、狗吼、雞叫、孩子哭
    /// </summary>
    public class Cat
    {
        private event Action MiaoHandler;
        public void MiaoEvent()
        {
            Console.WriteLine("{0} MiaoEvent.....", this.GetType().Name);
            if (this.MiaoHandler != null)
            {
                foreach (Action item in this.MiaoHandler.GetInvocationList())
                {
                    item.Invoke();
                }
            }
        }
    }
}

3、責任鏈模式(ResponsibilityChain Pattern)

責任鏈模式:請求的處理流程,沿著鏈子順序執行,還允許鏈子擴展和訂製。被稱為行為型設計模式的巔峰之作。

Context(上下文環境):用來保存業務處理中的參數--中間結果--最終結果。是行為型設計模式常用的標配。

為什麼說Context是行為型設計模式常用的標配呢?因為行為型設計模式關註的是對象和行為的分離,而方法(行為)在處理過程中就需要一些參數並且有返回值,這些信息就存放在Context上下文環境中。

場景:就以我們的請假審批流程為例,我們都知道不同的角色能夠審批的假期時長是不一樣的,例如:直接上級(SM)有權批1天的假期,主管(Charge)有權批3天的假期,經理(Manager)有權批7天的假期,總監(Chief)有權批30天假期,而我們的董事長(CEO)有權批300天的假期。下麵我們通過代碼來看下如何實現這個審批的功能。

首先我們來看下可能會想到的解決思路:

using System;

namespace ResponsibilityChainPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            //請假申請
            ApplyContext context = new ApplyContext()
            {
                Id = 1001,
                Name = "隔壁老王",
                Hour = 60,
                Description = "有急事需要請假幾天",
                AuditResult = false,
                AuditRemark = ""
            };

            {
                AbstractAuditor sm = new SM()
                {
                    Name = "直接上級"
                };
                sm.Audit(context);
                if (!context.AuditResult)
                {
                    AbstractAuditor charge = new Charge()
                    {
                        Name = "主管"
                    };
                    charge.Audit(context);
                    if (!context.AuditResult)
                    {
                        AbstractAuditor manager = new Manager()
                        {
                            Name = "經理"
                        };
                        manager.Audit(context);
                        if (!context.AuditResult)
                        {
                            //找下一環節
                        }
                    }
                }

                if (context.AuditResult)
                {
                    Console.WriteLine(context.AuditRemark);
                }
                else
                {
                    Console.WriteLine("不幹了!");
                }
            }
        }
    }
}

其實上面的這段代碼我們只是按照面向對象的思路翻譯了一遍,完全沒有設計、沒有加工、沒有思考。這裡就有一個很大的弊端,比如:此處隔壁老王請假60小時,首先他去找直接上級請假,發現直接上級沒有批假許可權。然後他又去找主管請假,發現主管也沒有批假許可權。接著他又要去找經理、找總監請假,如此找下去直到找到有批假許可權人員為止,這就很不符合常理。正確的思路應該是這樣的,隔壁老王直接將請假申請交給直接上級審批,對於直接上級來說,如果是許可權範圍內則審批通過,如果許可權範圍外則轉交下一環節審批。下一環節的審批人員也是如此。最終我們就可以將請求的處理流程,沿著鏈子順序執行。

接下來我們使用責任鏈模式來實現下這個功能:

請假申請上下文:

using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 請假申請
    /// Context(上下文環境):用來保存業務處理中的參數--中間結果--最終結果。是行為型設計模式常用的標配。
    /// 把行為轉移
    /// </summary>
    public class ApplyContext
    {
        public int Id { get; set; }

        /// <summary>
        /// 姓名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 請假時長
        /// </summary>
        public int Hour { get; set; }

        /// <summary>
        /// 請假理由
        /// </summary>
        public string Description { get; set; }

        /// <summary>
        /// 審核結果
        /// </summary>
        public bool AuditResult { get; set; }

        /// <summary>
        /// 審核備註
        /// </summary>
        public string AuditRemark { get; set; }
    }
}

抽象審核人員:

using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 抽象審核人員
    /// </summary>
    public abstract class AbstractAuditor
    {
        public string Name { get; set; }

        /// <summary>
        /// 審核
        /// </summary>
        public abstract void Audit(ApplyContext context);

        /// <summary>
        /// 下一個審核者
        /// </summary>
        private AbstractAuditor _nextAuditor = null;

        /// <summary>
        /// 指定下一個審核者
        /// </summary>
        public void SetNext(AbstractAuditor auditor)
        {
            this._nextAuditor = auditor;
        }

        /// <summary>
        /// 交給下一個審核人員審核
        /// </summary>
        protected void AuditNext(ApplyContext context)
        {
            if (this._nextAuditor != null)
            {
                this._nextAuditor.Audit(context);
            }
            else
            {
                context.AuditResult = false;
                context.AuditRemark = "不允許請假!";
            }
        }
    }
}

下麵我們不同角色的審核人員都去繼承抽象的審核人員,審核職責是:許可權範圍內則審批通過,許可權範圍外則轉交下一環節審批。

using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 直接上級
    /// 
    /// 職責問題:
    ///     1、許可權範圍內,審批通過。
    ///     2、許可權範圍外,轉交下一環節審批。
    /// </summary>
    public class SM : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            Console.WriteLine($"This is {this.GetType().Name} {this.Name} Audit");
            if (context.Hour <= 8)
            {
                context.AuditResult = true;
                context.AuditRemark = "允許請假!";
            }
            else
            {
                base.AuditNext(context); //轉交下一環節審批
            }
        }
    }
}
using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 主管
    /// </summary>
    public class Charge : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            Console.WriteLine($"This is {this.GetType().Name} {this.Name} Audit");
            if (context.Hour <= 24)
            {
                context.AuditResult = true;
                context.AuditRemark = "允許請假!";
            }
            else
            {
                base.AuditNext(context); //轉交下一環節審批
            }
        }
    }
}
using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 經理
    /// </summary>
    public class Manager : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            Console.WriteLine($"This is {this.GetType().Name} {this.Name} Audit");
            if (context.Hour <= 56)
            {
                context.AuditResult = true;
                context.AuditRemark = "允許請假!";
            }
            else
            {
                base.AuditNext(context); //轉交下一環節審批
            }
        }
    }
}
using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 總監
    /// </summary>
    public class Chief : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            Console.WriteLine($"This is {this.GetType().Name} {this.Name} Audit");
            if (context.Hour <= 240)
            {
                context.AuditResult = true;
                context.AuditRemark = "允許請假!";
            }
            else
            {
                base.AuditNext(context); //轉交下一環節審批
            }
        }
    }
}
using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 董事長
    /// </summary>
    public class CEO : AbstractAuditor
    {
        public override void Audit(ApplyContext context)
        {
            Console.WriteLine($"This is {this.GetType().Name} {this.Name} Audit");
            if (context.Hour <= 2400)
            {
                context.AuditResult = true;
                context.AuditRemark = "允許請假!";
            }
            else
            {
                base.AuditNext(context); //轉交下一環節審批
            }
        }
    }
}

最後來看下如何使用:

using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 建造者
    /// </summary>
    public class AuditorBuilder
    {
        public static AbstractAuditor Build()
        {
            AbstractAuditor sm = new SM()
            {
                Name = "直接上級"
            };
            AbstractAuditor charge = new Charge()
            {
                Name = "主管"
            };
            AbstractAuditor manager = new Manager()
            {
                Name = "經理"
            };
            AbstractAuditor chief = new Chief()
            {
                Name = "總監"
            };
            AbstractAuditor ceo = new CEO()
            {
                Name = "董事長"
            };

            //轉交下一環節,允許鏈子擴展和訂製
            sm.SetNext(charge);
            charge.SetNext(manager);
            manager.SetNext(chief);
            chief.SetNext(ceo);
            return sm;
        }
    }
}
using System;

namespace ResponsibilityChainPattern
{
    /// <summary>
    /// 責任鏈模式:請求的處理流程,沿著鏈子順序執行,還允許鏈子擴展和訂製。被稱為行為型設計模式的巔峰之作。
    /// </summary>
    class Program
    {
        static void Main(	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 代碼片段: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean if (!mbd.isSynthetic() && hasInstantiationAwareBeanPos ...
  • 屬性掃描完成之後, 就可以開始屬性註入了. 代碼塊: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean boolean earlySingletonExposure = ...
  • 創建完對象之後, 接下來, 就應該想辦法對屬性進行註入了, 其中就包括 @Autowired 註入 但是在註入之前, 貌似 還沒有對 @Autowired 進行掃描和解析. 代碼塊: if (instanceWrapper == null) { /** * 創建 bean 實例,並將實例包裹在 Be ...
  • 點此獲取下載地址提取碼:y9u5 Python網路編程最好新手入門書籍!175個詳細案例,事實勝於雄辯,Sockets、DNS、Web Service、FTP、Email、SMTP、POP、IMAP、SocketServe、CGI、XML、Forking、資料庫查詢手機客戶端、多核、非同步通信……詳細 ...
  • spring在創建對象(org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance)的時候, 使用了這個 構造函數後置處理器, 用來選擇使用哪個構造函數的. 所以這個後 ...
  • 接著前面, 看完構造函數前的後置處理器, 就到 doCreateBean 方法了. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] ...
  • 又是好久沒有寫題解了。。。。。 1.題意分析: P2299是一道非常經典的圖論最短路練習題。 圖論最短路是圖論中非常重要的一個知識模塊,其主要演算法有Dijkstra,Bellman-Ford,SPFA和Floyd。在這片題解中我們著重介紹Dijkstra演算法。 2.演算法詳解: Dijkstra應該是 ...
  • 一、Servlet 1. 什麼是Servlet Servlet 是 JavaEE 規範之一,規範就是介面 Servlet 就 JavaWeb 三大組件之一,三大組件分別是:Servlet 程式、Filter 過濾器、Listener 監聽器 Servlet 是運行在伺服器上的一個 java 小程式, ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...