五个设计模式的小demo
Go to file
2019-08-07 08:29:18 +00:00
DecoratorPattern firstTest 2019-08-07 14:52:35 +08:00
DesignPatterns@71b1c6b531 增加了代码 2019-08-07 14:25:13 +08:00
FactoryPattern firstTest 2019-08-07 14:52:35 +08:00
ObserverPattern firstTest 2019-08-07 14:52:35 +08:00
ProxyPattern firstTest 2019-08-07 14:52:35 +08:00
Singleton firstTest 2019-08-07 14:52:35 +08:00
UML类图 增加了UML图 2019-08-07 14:59:57 +08:00
.gitignore 增加了gitignore并测试 2019-08-07 15:13:14 +08:00
README.md 更新 'README.md' 2019-08-07 08:29:18 +00:00

DesignPatterns

1、单例模式

单例模式是属于创建型模式,提供了一种创建对象的最佳方式,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。保证一个类只有一个实例,并提供一个访问它的全局访问点

  • 单例类只能有一个实例
  • 单例类必须自己创建自己的这一实例
  • 单例类必须给所有其他对象提供这一实力

关键是:构造函数是私有的

主要解决:一个全局使用的类频繁的创建与销毁

优点:

  • 内存中仅有一个实例,减小内存开销
  • 避免资源的多重占用(写文件)

缺点:没有接口,不能继承,与单一职责原则冲突

实现:

  • 懒汉式:

    仅仅定义对象在get函数里进行实例化

    缺点就是线程不安全,解决办法就是:加锁 synchronized 保证单例;优点就是第一次调用才初始化避免了内存浪费

  • 饿汉式

    比较常用的方式,类加载时就初始化

    缺点是浪费内存;优点是没有加锁,效率提高

  • 双重校验锁

    这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

    首先先判断该类是否有实例化,然后如果没有再加锁进行实例化。

  • 登记式

最后采用的是饿汉式:

//TestSingleton.cs
class TestSingleton{
    //设置名字
    String name = null;  
    //创建实例并初始化
    private static TestSingleton instance = new TestSingleton();
    //构造函数
    private TestSingleton(){}
    //返回实例
    public static TestSingleton getInstance(){
        return instance;
    }

    public String getName() {  
        return name;  
    }  

    public void setName(String name) {  
        this.name = name;  
    }  

    //输出name
    public void printInfo() {  
        Console.WriteLine("the name is " + name);  
    }  
}

//SingletonMain.cs
class SingletonMain{
    static void Main(string[] args){
        //获得实例
        TestSingleton test1 = TestSingleton.getInstance();
        //设置姓名
        test1.setName("Jack");
        //获得实例
        TestSingleton test2 = TestSingleton.getInstance();
        test2.setName("Jane");

        //输出名字
        test1.printInfo();
        test2.printInfo();

        //判断是否为同一对象
        if(test1 == test2){
            Console.WriteLine("为同一个实例");
        } else{
            Console.WriteLine("Hello World!");
        }            
    }
}

UML类图

2、工厂模式

创建型模式。在工厂模式中,我们创建对象时不会对客户端暴露创建逻辑,而是通过共同的接口来指向新创建的对象。定义一个创建对象的接口,让其子类自己觉得实例化哪一个工厂类。

主要解决接口选择问题。

优点:调用者需要创建一个对象只需知道名称就可以了;扩展性高,如果想要增加一个产品,只需要扩展一个工厂类就可以;对于产品的具体实现对调用者来说是不可见的。

缺点:每次增加一个产品就需要增加一个具体的类和对象实现工厂,增加了复杂度和具体类的依赖。

注意复杂对象适用于工厂模式对于简单的对象只需要new就可以进行创建。

实现创建一个Drinks的接口和实现Drinks接口的实体类还需要定义工程类DrinksFactory

//Drinks接口
public interface Drinks
{
    //定义饮料类型
    string name { get; set; }
    //定义方法
    void make();
}
//实现Drinks的实体类
class Coffee : Drinks
{
    public string name { get; set; }
    public Coffee()
    {
    	name = "coffee";
    }
	public void make()
    {
        Console.WriteLine("This is "+name);
    }
}

class MilkTea :Drinks
{
	public string name { get; set; }
    public MilkTea()
    {
        name = "milk tea";
    }
    public void make()
    {
        Console.WriteLine("This is " + name);
    }
}
//工厂类
class DrinksFactory
{
    public Drinks getDrinks(String drinkType)
    {
        //判断是否为空
        if (drinkType == null) 
        {
            return null;
        }
        //根据输入的类型实例化对象
        if (drinkType.Equals("COFFEE"))
        {
            return new Coffee();
        }else if (drinkType.Equals("MILKTEA"))
        {
            return new MilkTea();
        }
        return null;
    }
}
class FactoryPatternDemo
{
    static void Main(string[] args)
    {
        DrinksFactory drinkShop = new DrinksFactory();
        //获取咖啡对象
        Drinks coffee = drinkShop.getDrinks("COFFEE");
        coffee.make();
        //获取奶茶对象
        Drinks milkTea = drinkShop.getDrinks("MILKTEA");
        milkTea.make();

    }
}

UML类图:

3、抽象工厂模式

抽象工厂模式Abstract Factory Pattern是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式它提供了一种创建对象的最佳方式。在抽象工厂模式中接口是负责创建一个相关对象的工厂不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

**优点:**当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

**缺点:**产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

我的理解:将上面的工厂模式,再抽象以下,有多个工厂生产不同的物品,每一个物品又有不同的类别。

4、代理模式

代理模式给某一个对象

提供一个代理对象,并由代理对象控制对原对象的引用。针对某些情况,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介作用。符合代码的开闭原则

  • 静态代理

    1. 创建服务类接口
    2. 实现服务接口
    3. 创建代理类
    4. 编写测试类

    优点:可以做到在符合开闭原则的情况下对目标对象功能进行扩展

    缺点:需要为每一个服务创建代理类,工作量太大

  • 动态代理

    Castle代理

    //抽象商店接口
    public interface Shop
    {
        //购买饮料
        void BuyDrinks();
    }
    
    //实现奶茶店类
    class MilkTeaShop:Shop
    {
        public virtual void BuyDrinks()
        {
        	Console.WriteLine("奶茶店收取费用");
        }
    }
    
    //中介类
    public class ProxyShop:IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            //执行奶茶店的逻辑
            invocation.Proceed();
            CollectIntermediaryFees();
        }
    
        private void CollectIntermediaryFees()
        {
        	Console.WriteLine("中介收取配送费!");
        }
    }
    
     class ByDrinksProxy 
     {
         static void Main(string[] args)
         {
             Console.WriteLine("外卖中介帮你买奶茶");
             Shop proxyShop = CreateShopProxy();
             Console.WriteLine("中介成功出购买奶茶");
             proxyShop.BuyDrinks();
         }
    
         private static Shop CreateShopProxy()
         {
             ProxyGenerator proxyGenerator = new ProxyGenerator();
             Shop proxyShop = proxyGenerator.CreateClassProxy<Shop>(new ProxyShop());
             return proxyShop;
         }
     }
    
    

5、观察者模式

当对象存在着一对多的关系时,使用观察者模式。属于行为型模式,当一个对象状态发生改变时,所有依赖于它的对象都得到通知并自动更新。

优点:观察者和被观察者是抽象耦合的,建立一套触发机制

缺点:通知所有观察者会花费很多时间;有可能存在循环依赖;观察者模式仅仅知道发生了变化,但是不知道如何发生变化。

实现:

//观察者的抽象类
abstract class Observer
{
    //被观察者
    protected Material material;
    //更新价格
    public abstract void update();
}
//实例化的两个观察类
class Product1 : Observer
{
    public Product1(Material material){
        this.material = material;
        this.material.attach(this);
    }
    public override void update()
    {
        //计算商品价格
        int price = 13 + this.material.getPrice();
        Console.WriteLine("商品1价格更新为"+price);
    }
}
class Product2 : Observer
{
    public Product2(Material material)
    {
        this.material = material;
        this.material.attach(this);
    }
    public override void update()
    {
        //计算商品价格
        int price = 18 + this.material.getPrice();
        Console.WriteLine("商品2价格更新为"+price);
    }
}

//被观察着对象
class Material
{
    //所有商店的集合
    private List<Observer> shops = new List<Observer>();
    private int price { get; set; }
    //返回原材料的价格
    public int getPrice()
    {
        return price;
    }
    //设置原材料的价格并更新商店的价格
    public void setPrice(int price)
    {
        this.price = price;
        this.updateAllPrice();
    }
    //增添观察者也就是商店
    public void attach(Observer observer)
    {
        shops.Add(observer);
    }
    //更新价格
    public void updateAllPrice()
    {
        foreach (Observer observer in shops)
        {
            observer.update();
        }
    }
}

class ObserverPatternDemo
{
    static void Main(string[] args)
    {
        Material material = new Material();
        new Product1(material);
        new Product2(material);
        Console.WriteLine("原材料价格改为15");
        material.setPrice(15);
        Console.WriteLine("原材料价格改为18");
        material.setPrice(18);

    }
}

UML

6、装饰器模式

装饰器模式允许一个向现有的一个对象添加新的功能,同时不修改结构,属于结构型模式。动态的给一个对象添加一些额外的职责。

优点 :装饰类和被装饰类可以独立发展,不会耦合,动态的扩展一个实现类的功能

缺点:多层装饰比较复杂

实现:

//创建一个接口
public interface Product
{
    //价格
    int price { get; set; }
    //名字
    String name { get; set; }
    void make();
}

//创建实现接口的被装饰类
class MilkGreenTea : Product
{
	public int price { get ; set; }
    public String name { get; set; }
    public MilkGreenTea()
    {
    	//初始化价格
        price = 15;
        name = "奶绿";
	}
    public void make()
    {
    	Console.WriteLine(("你的产品为:" + name);
    }
}
class MilkRedTea:Product
{
    public int price { get; set; }
    public String name { get; set; }
    public MilkRedTea()
    {
        //初始化价格
        price = 12;
        name = "奶茶";
    }
    public void make()
    {
    	Console.WriteLine("你的产品为:"+this.name);
    }
}

//装饰类的抽象类
public abstract class Additives : Product
{
    public int price { get ; set ; }
    public String name { get; set; }
    //被装饰的物品
    protected Product product;
    public Additives(Product product)
    {
    	this.product = product;
    }
    public void make()
    {
        Console.WriteLine("你的产品为:" + this.name);
        Console.WriteLine("你的价格:" + this.price);
    }
}

//创建扩展的实体修饰类
class Pearl : Additives
{
	public Pearl(Product product) : base(product)
    {
		this.name = this.product.name + "+" + "珍珠";
        this.price = this.product.price + 5;
	}
}
class Pudding : Additives
{
    public Pudding(Product product) : base(product)
    {
        this.name = this.product.name + "+布丁" ;
        this.price = this.product.price + 6;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Additives drink1 = new Pudding(new Pearl(new MilkGreenTea()));
        Additives drink2 = new Pudding(new Pearl(new MilkRedTea()));
        Additives drink3 = new Pearl(new Pearl(new MilkRedTea()));
        drink1.make();
        drink2.make();
        drink3.make();
    }
}

UML图

7、责任链模式

责任链模式Chain of Responsibility Pattern为请求创建了一个接收者对象的链。这种模式给予请求的类型对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。

在这种模式中通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求那么它会把相同的请求传给下一个接收者依此类推。JS 中的事件冒泡

优点1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响而且在进行代码调试时不太方便可能会造成循环调用。 3、可能不容易观察运行时的特征有碍于除错。