C#设计模式

news2024/9/20 18:41:56

前言

大家熟知的GOF23种设计模式,源自《Design Patterns: Elements of Reusable Object-Oriented Software》一书,由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著,四人组Gang of Four简称GOF!总结了在面 向对象语言开发过程中常见问题的解决方案! 设计模式是面向对象语言开发过程中,遇到的种种场景和问题,然后提出的思 路和解决方案,最后沉淀下来,就成了设计模式--- 有具体招数可循; 设计模式其实就是解决问题的思路,是前辈总结出来的有效方式方法,就是套 路! 学习设计模式,就是为了站在前辈的肩膀上,能更快捷优雅的解决面向对 象程序开发设计问题。

设计模式分类

三大类

  1. 创建型设计模式
    1. 关注对象的创建
  1. 行为型设计模式
    1. 关注类与类之间的关系
  1. 结构型设计模式
    1. 关注对象和行为的分离

套路

设计模式六大原则与23种设计模式的关系 设计模式是解决问题的具体套路,降龙十八掌 六大原则是程序设计的指导原则,道家的小无相功, 没有具体招数,没有具体的标准。

类和类之间的关系

单个类没有任何意义!面向对象决定了类和类之间的各种关系!

横向:平级关系

  1. 组合关系 :

组合也是关联关系的一种特例,它体现的是一 种contains-a的关系,这种关系比聚合更强,也称为强聚合。 例如:人与心脏

  1. 聚合关系(Aggregation):

表示的是整体和部分的关系, 整体与部分是可以分开(has-a)。

例如:车和发动机

  1. 关联关系(Association):

两个类之间语义级别的一种强依 赖关系。

例如:班级与学生、球员与球队

  1. 依赖关系(Dependence):

假设A类的变化引起了B类的变 化,则说名B类依赖于A类。---方法里面/参数,是一种很淡 的关联---单向的依赖

纵向:层级关系

  1. 继承
    1. 强继承:任何子类继承父类,都必须拥有父类的一切行为 和属性
  1. 实现
    1. 强实现:子类实现接口,必须实现接口的方法

关系:非常紧密

类和类的强弱关系

继承≈实现 >组合>聚合>关联>依赖

结构型设计模式常规套路:组合优于继承!

结构型设计模式

7种结构型设计模式,关注类与类之间的关系 其实就是折腾组合与继承,(组合优于继承) 为程序提供更好的灵活性和扩展性。

Adapter Pattern(适配器)

将一个接口转换成客户希望的另一个接口,使接口不兼容的 那些类可以一起工作,其别名为包装器(Wrapper)。

场景:

现在很多操作几乎是重复的调用API和自己封装好API,例如数据库操作,都是CRUD操作,但是如果要进行优化,是用了第三方库的时候,方法名字是和自己封装的名字是对应不上的,但是也想要使用,所以需要适配器类才能让他和目前的方法进行适配。

现存已有的方法:

public interface IHelper
{
    void Add<T>();
    void Delete<T>();
    void Update<T>();
    void Query<T>();
}

需要适配的方法:

 public class RedisHelper
 {
     /// <summary>
     /// 第三方提供的sdk
     /// </summary>
     public RedisHelper()
     {
         Console.WriteLine("构造RedisHelper");
     }
     public void AddRedis<T>()
     {
         Console.WriteLine($"This is {this.GetType().Name} AddRedis");
     }

     public void DeleteRedis<T>()
     {
         Console.WriteLine($"This is {this.GetType().Name} DeleteRedis");
     }

     public void QueryRedis<T>()
     {
         Console.WriteLine($"This is {this.GetType().Name} QueryRedis");
     }

     public void UpdateRedis<T>()
     {
         Console.WriteLine($"This is {this.GetType().Name} UpdateRedis");
     }
 }

类适配器

原理:继承第三方库和现有的方法接口,然后可以实现接口,调用第三方库的方法

缺点:由于继承了第三方类库,就导致拥有了第三方类库的方法,这样的话不安全,具有一定的侵入性

public class RedisClassAdapter : RedisHelper, IHelper
{
    public void Add<T>()
    {
        base.AddRedis<T>();
    }

    public void Delete<T>()
    {
        base.DeleteRedis<T>();
    }

    public void Query<T>()
    {
        base.QueryRedis<T>();
    }

    public void Update<T>()
    {
        base.UpdateRedis<T>();
    }
}

使用方法:

IHelper helper = new RedisClassAdapter();
helper.Add<Program>();
helper.Delete<Program>();
helper.Update<Program>();
helper.Query<Program>();

对象适配器

原理:在类中,写一个需要适配的类的实例化,然后实现接口,调用实例化中的方法。

建议使用对象适配器,能够防止方法泄露,而且可以复用,根据构造函数进行传值可以多种类一起使用。

/// <summary>
/// Redis 对象适配器
/// </summary>
public class RedisObjectAdapter : IHelper
{
    private readonly RedisHelper redisHelper = new RedisHelper();

    //public RedisObjectAdapter(RedisHelper _redisHelper)
    //{
    //    redisHelper = _redisHelper;
    //}

    public void Add<T>()
    {
        redisHelper.AddRedis<T>();
    }

    public void Delete<T>()
    {
        redisHelper.DeleteRedis<T>();
    }

    public void Query<T>()
    {
        redisHelper.QueryRedis<T>();
    }

    public void Update<T>()
    {
        redisHelper.UpdateRedis<T>();
    }
}

总结:

适合做重构和接入新的东西。

对象适配器 > 类适配器

Bridge(桥接)

Composite(组合)

Decorator(装饰)

原理:组合+继承~~ 完成层层装饰~~

代码:

基础抽象类:

public abstract class AbstractStudent// : Object
{
    public int Id { get; set; }
    public string Name { get; set; }

    public abstract void Study();
}

继承抽象类代码:

 public class StudentVip : AbstractStudent
 {
     /// <summary>
     /// 付费  上课前要预习   
     /// 上课学习
     /// </summary>
     public override void Study()
     {
         Console.WriteLine("{0} is a vip student studying .net Vip", Name);
     }
 }

装饰器类:继承基础抽象类,证明这个装饰类也是学生类

 public abstract class AbstractDecorator : AbstractStudent// 装饰后还是一个学生
 {
     private AbstractStudent _Student = null;
     public AbstractDecorator(AbstractStudent student) : base()
     {
         _Student = student;
     }
     public override void Study()
     {
         //Console.WriteLine("***********************");
         _Student.Study();
         //Console.WriteLine("***********************");
     }
 }

装饰器若干:

可以写多种修饰器代码,然后执行父类的Study方法之后写入自己的修饰。

public class StudentDecoratorVideo : AbstractDecorator
{
    public StudentDecoratorVideo(AbstractStudent student) : base(student)
    {
    }
    public override void Study()
    {
        base.Study();
        Console.WriteLine("获取视频代码。。。");
    }
}

若干:。。。。。。。。。。

使用代码:

AbstractStudent student = new StudentVip()
{
    Id = 123,
    Name = "小海"
};

// 装饰的顺序不一样 执行也不一样
// 多个装饰 随意修饰
student = new StudentDecoratorVideo(student);
student = new StudentDecoratorAnswer(student);
student = new StudentDecoratorHomework(student);

student.Study();

效果:

Facade(外观/门面)

Flyweight(享元)

Proxy(代理)

给某一个对象提供一个代理对象,并由代理对象控制对原对 象的引用。

例如:

  • 地产代理
  • 火车票代售点
  • 翻墙梯子
  • VPN

代码:

业务接口:

public interface ISubject
{
    /// <summary>
    /// get
    /// </summary>
    /// <returns></returns>
    bool GetSomething();

    /// <summary>
    /// do
    /// </summary>
    void DoSomething();
}

现有的原本类的方法:

public class RealSubject : ISubject
{
    public RealSubject()
    {
        Thread.Sleep(2000);
        long lResult = 0;
        for (int i = 0; i < 100000000; i++)
        {
            lResult += i;
        }
        Console.WriteLine("RealSubject被构造。。。");
    }

    /// <summary>
    /// 火车站查询火车票
    /// </summary>
    public bool GetSomething()
    {
        Console.WriteLine("坐车去火车站看看余票信息。。。");
        Thread.Sleep(3000);
        Console.WriteLine("到火车站,看到是有票的");
        return true;
    }

    /// <summary>
    /// 火车站买票
    /// </summary>
    public void DoSomething()
    {
        //Console.WriteLine("This is DoSomething Before");
        //try
        //{


        Console.WriteLine("开始排队。。。");
        Thread.Sleep(2000);
        Console.WriteLine("终于买到票了。。。");
        //}
        //catch (Exception)
        //{

        //    throw;
        //}
    }
}

代理模式,将现有的类包一层代码:需要使用一个缓存类库

public class ProxySubject : ISubject
{
    /// <summary>
    /// RealSubject只实例化一次,单例代理
    /// </summary>
    private static ISubject _iSubject = new RealSubject();

    /// <summary>
    /// 缓存代理--提升性能
    /// 查询票
    /// </summary>
    /// <returns></returns>
    public bool GetSomething()
    {
        //bool bResult = _iSubject.GetSomething(); 
        //return bResult;
        
        string key = $"{nameof(ProxySubject)}_{nameof(GetSomething)}";
        bool bResult = false;
        if (CustomCache.Exist(key))
        {
            bResult = CustomCache.Get<bool>(key);
        }
        else
        {
            bResult = _iSubject.GetSomething();
            CustomCache.Add(key, bResult);
        }

        return bResult;
        return _iSubject.GetSomething();
    }


    /// <summary>
    /// 日志--异常处理--只要通过Proxy来调用 就能获得这两个功能
    /// 买票
    /// </summary>
    public void DoSomething()
    {
        try
        {
            Console.WriteLine("This is DoSomething Before");
            _iSubject.DoSomething();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            throw;
        }
    }
}

缓存类库:用来判断这个代理有没有加入

public class CustomCache
{
    private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>
        ();
    public static void Add(string key, object value)
    {
        CustomCacheDictionary.Add(key, value);
    }
    public static T Get<T>(string key)
    {
        return (T)CustomCacheDictionary[key];
    }
    public static bool Exist(string key)
    {
        return CustomCacheDictionary.ContainsKey(key);
    }
}

总结:

使用情况:

可以增加一些公共的业务逻辑,而不修改历史代码,可以在原来的逻辑之前或者之后进行增加新的业务。

结构型设计模式:

通过新的类来进行包装; 包一层大法; 没有任何问题是包一层不能解决的,如果不能解决,再包一层;

行为型设计模式

11种行为型设计模式,是最大 的一个家族了。 行为型设计模式关注的是对象和 行为的分离,直白点说就是行为(方 法)是放在这个类里面,还是那个类 里面,关注的内容更细腻,因此套 路也更多!

核心:做行为的转移,做甩锅。

Interpreter(解释器)

Template Method(模板方法)

Chain of Responsibility(责任链)

使多个对象都有处理请求的机会,从而避免了请求的发送者和接收 者之间的耦合关系,将这些对象串成一条链,并沿着这条链一直传 递该请求,直到有对象处理它为止。 行为型设计模式的巅峰之作,无止境的行为封装转移!

场景模拟:

请假-审批流程

Context上下文环节,行为型设计模式的标配

因为行为型模式是关注对象和行为的分离 也就是把方法转移来转移去 那些参数信息和中间结果就得存一下 这就是Context了

审批业务逻辑转移 :

上下文保存变量类:

public class ApplyContext
{
    public int Id { get; set; }
    public string Name { get; set; }
    /// <summary>
    /// 请假时长
    /// </summary>
    public int Hour { get; set; }
    public string Description { get; set; }
    public bool AuditResult { get; set; }
    public string AuditRemark { get; set; }
}

基础业务逻辑:

很明显的问题:面向过程编程,完全没有封装。。

Console.WriteLine("找项目经理审批");
if (context.Hour <= 8)
{
    Console.WriteLine("PM审批通过");
}
Console.WriteLine("找主管审批");
if (context.Hour <= 16)
{
    Console.WriteLine("主管审批通过");
}
else
{
    //..... 继续往后去找人审批
    Console.WriteLine("************************************");
}
封装-继承-多态

把每层封装为一个类:相同的写一个基类里面

public abstract class AbstractAuditor
{
    public int Id { get; set; }
    public string Name { get; set; }

    // 因为必须有此方法 所以必须是抽象方法abstract修饰 
    public abstract void Audit(ApplyContext context);

    /// <summary>
    /// 下一个审批者
    /// </summary>
    protected AbstractAuditor _NextAuditor;

    /// <summary>
    /// 用作设置下一个审批者
    /// </summary>
    /// <param name="nextAuditor">下一个审批者</param>
    public void SetNextAuditor(AbstractAuditor nextAuditor)
    {
        _NextAuditor = nextAuditor;
    }
}

PM类:

 public class PM : AbstractAuditor
 {

     public override void Audit(ApplyContext context)
     {
         if (context.Hour <= 8)
         {
             context.AuditResult = true;
             context.AuditRemark = "PM审批通过";
         }
         else
         {
             _NextAuditor.Audit(context);   //把新一个要审批的行为转移走了;
         }
     }
 }

..........省略一百层。。。。

使用:

设置每一层的人员,并且在上层自己设置下一级是哪个人。

AbstractAuditor pM = new PM()
{
    Id = 123,
    Name = "其乐无穷"
};
AbstractAuditor charge = new Charge()   //请假者自己去找主管审批
{
    Id = 234,
    Name = "诺"
};
AbstractAuditor manager = new Manager()
{
    Id = 345,
    Name = "麦子熟了"
};
pM.SetNextAuditor(charge);
charge.SetNextAuditor(manager);

 设置完下一层的时候 执行下审批
pM.Audit(context);
从POP到OOP的升级
从菜鸟走向中级开发

使用场景:

如果一个流程有很多个环节,业务由其中一个或者多个协作处理, 就可以选择责任链模式!

  1. 流程审批
  2. 工资结算
  3. 管道处理模型

总结:

甩锅大法~~ 为了保证自己的稳定; 在这个类中不去固定某一个 类型,而是让别人来执行;

Command(命令)

Iterator(迭代器)

Mediator(中介者)

Memento(备忘录)

Observer(观察者)

业务背景:

一只神奇的猫

猫叫一声之后触发 一系列的行为:

1、小孩儿哭了

2、兄弟醒了

3、狗叫了

4、爸爸生气了~·

5、邻....

要求:以面向对象的想来模拟当前的这个场景:

代码:

基础代码:

很明显,Miao的时候确实都触发了,但是职责太多,猫应该只做自己的事情,其他的动作甩给上层处理

public void Miao()
{
    Console.WriteLine("{0} Miao.....", this.GetType().Name);
    new Mouse().Run();//猫和老鼠是啥关系--依赖
    new Chicken().Woo();
    new Baby().Cry();
    new Brother().Turn();
    new Dog().Wang();
    new Father().Roar();
    new Mother().Whisper();
    new Neighbor().Awake();
    new Stealer().Hide();
}
修改为观察者模式OOP:
public List<IObserver> observerlist = new List<IObserver>();
public void MiaoObserver()
{
    Console.WriteLine("{0} MiaoObserver.....", this.GetType().Name);
    foreach (var observer in observerlist) //具体执行的是什么玩意, 猫不关注;  集合中有什么行为,就执行什么行为;
    {
        observer.Action();
    }
}

IObserver接口:

/// <summary>
/// 只是为了把多个对象产生关系,方便保存和调用
/// 方法本身其实没用
/// </summary>
public interface IObserver
{
    void Action();
}

使用:

已经把刚才的Miao给拆开,而且做到了任务单一,把一些任务甩给调用层,上一层

Cat cat = new Cat(); 
cat.observerlist.Add(new Baby());
cat.observerlist.Add(new Brother());
cat.observerlist.Add(new Chicken());
cat.observerlist.Add(new Dog());
cat.observerlist.Add(new Father());
cat.observerlist.Add(new Mother());
cat.MiaoObserver(); 
修改为委托实现:
public Action ActionHander;
public void MiaoDelegate()
{
    Console.WriteLine("{0} MiaoDelegate.....", this.GetType().Name);
    ActionHander?.Invoke();
}

委托使用:

Cat cat = new Cat();
cat.ActionHander += new Baby().Action;
cat.ActionHander += new Brother().Action;
cat.ActionHander += new Chicken().Action;
cat.ActionHander += new Dog().Action; 
cat.ActionHander += new Father().Action;
cat.ActionHander += new Mother().Action;
cat.ActionHander += new Cat().MiaoDelegate;
//cat.ActionHander.Invoke(); //可以直接执行委托

cat.MiaoDelegate();
注意:

本质OOP和委托没有区别

OOP: 把一堆的对象保存起来

委托:把方法包装到委托中去了------委托的本质:其实是一个类;

修改为事件:
public event Action ActionHanderEvent;
public void MiaoEventHander()
{
    Console.WriteLine("{0} MiaoEventHander.....", this.GetType().Name);
    ActionHanderEvent?.Invoke();
}

事件使用:

Cat cat = new Cat();
cat.ActionHanderEvent += new Baby().Action;
cat.ActionHanderEvent += new Brother().Turn;
cat.ActionHanderEvent += new Chicken().Woo;
cat.ActionHanderEvent += new Dog().Wang;
cat.ActionHanderEvent += new Father().Roar;
cat.ActionHanderEvent += new Mother().Whisper;
cat.ActionHanderEvent += new Cat().MiaoDelegate;
//cat.ActionHanderEvent.Invoke();  //不允许的

//cat.MiaoEventHander();
委托和事件的区别:

事件---执行的执行,只允许在当前定义时间的这个类里面执行,不允许在外面执行,就是子类都不能执行;

搭建框架的时候,需要一些权限的管控; WPF 控件 Winform 控件里面;+= 方法的时候,都是事件,而不是委托;

State(状态)

Strategy(策略)

Visitor(访问者)

创建型设计模式

6种创建型设计模式,关注对象的创建, 其实就是如何new一个对象的问题, 这里也是有非常多的套路

Singleton(单例)

介绍:

就是整个程序有且仅有一个实例。该类负责创建自己的对象, 同时确保只有一个对象被创建。 自己创建自己,保证在进程中只有这个一个对象; 结构最简单的设计模式,没有之一。

单例:双判断+锁

业务背景:

有些时候重复New对象导致重置对象或者说浪费内存,则需要单例模式,但是由于多线程的时候

代码:
public class SingletonBase<T> where T : class, new()
{
    private static T _Instance;

    private static object ObjLock = new object();

    /// <summary>
    /// 双判断+锁
    /// </summary>
    /// <returns></returns>
    public static T CreateInstanc()
    {
        if (_Instance == null)
        {
            // 由于考虑到多线程的线程安全问题 所以使用加锁 方式处理
            lock (ObjLock)
            {
                if (_Instance == null)
                {
                    _Instance = new T();
                }
            }
        }
        return _Instance;
    }
}

静态构造函数 单例:

静态构造函数:特点

在整个进程中,执行且只执行一次的;

所以在静态函数中返回单例。

代码:

private SingletonSeoncd()
{
    long lResult = 0;
    for (int i = 0; i < 10000000; i++)
    {
        lResult += i;
    }
    Thread.Sleep(2000);
    Console.WriteLine($"{GetType().Name}被构造一次 {Thread.CurrentThread.ManagedThreadId}");
}

/// <summary>
/// 静态构造函数:特点 
///     在整个进程中,执行且只执行一次的;
/// </summary>
static SingletonSeoncd()
{
    _SingletonSeoncd = new SingletonSeoncd();
}

public static SingletonSeoncd CreateInstance()
{
    return _SingletonSeoncd;
}

// 静态类属性 返回
private static SingletonSeoncd _SingletonSeoncd;

public int Id { get; set; }
public string Name { get; set; }

public static void Show()
{
    Console.WriteLine(" Show 方法~~");
}

初始化的时候声明静态变量 单例:

public static SingletonThird CreateInstance()
{
    return _SingletonThird;
}

private static SingletonThird _SingletonThird = new SingletonThird();

应用场景:

线程池、数据库连接池、配置文件对象、IOC容器实例 等需要单例的

New个对象不费什么事儿 请不要画蛇添足,没有必须单例的,请勿单例!

总结:

  1. 私有化构造函数
  2. 对外提供获取实例的方法
  3. 提供静态变量,实例重用

Factory Method(工厂方法)

介绍:

通过定义工厂父类负责定义创建对象的公共接口, 而子类则负责生成具体的对象。 每一个不同的的对象分别自定义个工厂+抽象;

Abstract Factory(抽象工厂)

介绍:

在抽象工厂模式中,接口是负责创建一个相关对象的工厂。 每个生成的工厂都能按照工厂模式提供对象。 工厂+约束!

Builder(建造者)

介绍:

是将一个复杂的对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示, 创建者模式隐藏了复杂对象的创建过程。

核心: 就是灵活应用;

有之前的审批流程代码:

由于也需要修改代码进行改变组织架构,所以引进建造者模式,进行灵活使用

代码:

IBuilder接口:

/// <summary>
/// 建造者;可能需要创建很多对象,然后要组装;
/// </summary>
public interface IBuilder
{

    public AbstractAuditor CreateCharge();

    public AbstractAuditor CreateManager();

    //建造者:最终要建造一个审批的开始节点; 
    public AbstractAuditor Build();
}

每一个新的建造者:

public class AuditorWorkFlowsBuild : IBuilder
{
    public AbstractAuditor Build()
    {
        AbstractAuditor pm = new PM();
        AbstractAuditor charge = CreateCharge();
        AbstractAuditor manager = CreateManager();
        pm.SetNextAuditor(charge);
        charge.SetNextAuditor(manager);
        return pm;
    }

    public AbstractAuditor CreateCharge()
    {
        return new Charge();
    }

    public AbstractAuditor CreateManager()
    {
        return new Manager();
    }
}

使用:

说明:

可以写多个建造者,所以在切换组织的时候,建造者切换即可。

//如果组织架构发生变化;
//审批历程发生改变;  可以直接换掉建造者;
IBuilder builder = new AuditorWorkFlowsBuild();
AbstractAuditor pm = builder.Build();
 pm.Audit(context);

Prototype(原型)

介绍:

是用于创建重复的对象,同时又能保证性能。专门用来批量生产对象!

原理:

既要快速获得对象实例 又要对象直接不能互相干扰

copy大法:

MemberwiseClone

代码:

在创建实例的时候,不用返回上一个实例,直接复制一个即可,这样既能保证实例是最新的,也能保证快。

 public static SingletonPrototype CreateInstance()
 {
     //MemberwiseClone: 内存拷贝;  不是浅拷贝
     object oInstance = _SingletonPrototype.MemberwiseClone();
     return (SingletonPrototype)oInstance;
 }


 private static SingletonPrototype _SingletonPrototype = new SingletonPrototype();

创建对象的方式:

  1. new()
  2. 克隆
  3. 反序列化
  4. 反射

Simple Factory(简单工厂)

介绍:

是通过专门定义一个类来负责创建其他类的实例, 被创建的实例通常都具有共同的父类。

业务背景:

继续请假审批流程:

由于使用建造者的时候,修改建造者也比较繁琐,所以进一步优化,改为配置文件即可。

代码:

/// <summary>
/// 这个工厂是为了来创建建造者的
/// </summary>
public class SimpBuidlerFactory
{
    // 建造者的dll库
    private static string dllName = "XH.DesignPattern.ResponsibilityChainPattern.dll";
    // 建造者的类名:可以包装为配置文件 修改配置文件即可
    private static string typeName = "XH.DesignPattern.ResponsibilityChainPattern.Builder.AuditorWorkFlowsBuild";

    public static IBuilder CreateBuilderInstance()
    {
        //字符串:  写到配置文件中去;
        //          修改配置文件的信息,不用重新编译,可以直接生效 
        //return new AuditorWorkFlowsNewBuild(); 
        //反射
        Assembly assembly = Assembly.LoadFrom(dllName); // 获取DLL 
        Type type = assembly.GetType(typeName);// 获取DLL中的当前文件类型
        object oinstance = Activator.CreateInstance(type);// 创建对象
        return oinstance as IBuilder;
    }
}

使用:

IBuilder builder = SimpBuidlerFactory.CreateBuilderInstance(); 
AbstractAuditor pm = builder.Build();

pm.Audit(context);

总结:

结果: 如果组织架构发生改变; 只需要实现新的建造者,然后修改配置文件即可,根本就不用去修改历史代码;

这才是设计模式的正确使用方式;

设计模式总结:

结构型:包一层

行为型:甩锅大法

创建型:灵活运用

设计模式真的应该分3大类,创建型-结构型-行为型,就三类 招数

所以,23种设计模式,大家不要着相,不用去背, 其实学会三个核心套路,见招拆招,有问题不要想着先用什 么设计模式,先看看要做到什么,然后用什么核心套路,然 后根据特点微调,设计模式应该是自然而然 设计模式不是完美的,是为了解决一类问题而存在,通常在 解决一类问题时,还会带来其他问题,设计者要做的就是扬 长避短,比如设计模式融合,比如其他技术手段,最终解决 问题---程序设计,其实就是走一步看一步,不是一蹴而就

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2091989.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【例002】利用MATLAB绘制有趣的空间曲线

题目&#xff1a;利用MATLAB绘制以下函数的空间曲线 空间曲线1&#xff1a; { x cos ⁡ ( 3 t ) y sin ⁡ ( 3 t ) for − m 1000 ≤ t ≤ m 1000 z t \begin{cases} x \cos(3t) \\ y \sin(3t) & \text{for } -\frac{m}{1000} \leq t \leq \frac{m}{1000} \\ z t …

心理健康问答系统-AIGC大模型-小程序制作

制作一个心理健康问答系统的小程序&#xff0c;涉及到多个环节和技术领域。这里将从需求分析、技术选型、开发流程、API调用等方面进行详细说明。 一、需求分析与规划 在开始任何项目之前&#xff0c;首先需要明确的是你的小程序想要解决什么样的问题&#xff0c;提供哪些功…

Spring——控制反转(IOC)与依赖注入(DI)

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

【C++题解】1722 - 输出两位的巧数

问题&#xff1a;1722 - 输出两位的巧数 类型&#xff1a;简单循环 题目描述&#xff1a; 巧数指的是这样一种特殊的数&#xff0c;该数的各个位数字之和加上各个位数字之积等于本身的自然数。 比如整数 19 &#xff0c;就是一个巧数&#xff0c;因为(19)(19)10919。 请编程输…

DataWhale AI夏令营 2024大运河杯-数据开发应用创新赛-task3

DataWhale AI夏令营 2024大运河杯-数据开发应用创新赛 数据增强数据收集打标签 多的不说少的不唠&#xff0c;之前说过初赛基本就是比谁的数据好了&#xff0c;因为原始数据的质量太低了想跑到0.25都很难所以需要使用一些数据增强策略以及收集一些新的数据集。 数据增强 计算…

LVGL 控件之按钮(lv_button)

目录 一、按钮1、概述2、样式2.1 设置背景2.1.1 颜色2.1.2 透明度2.1.3 渐变色2.1.4 渐变色起始位置设置 2.2 修改边界2.2.1 宽度2.2.2 颜色2.2.3 透明度2.2.4 指定边 2.3 修改边框2.4 修改阴影2.4.1 宽度2.4.2 透明度2.4.3 偏移坐标2.4.4 颜色2.4.5 延伸 2.5 设置圆角弧度2.6 …

C++STL~~list

文章目录 一、list的概念二、list的使用三、list的练习四、与vector的对比五、总结 一、list的概念 list 是一种容器&#xff0c;实现了双向链表结构 它具有以下特点&#xff1a; 动态大小&#xff0c;可按需增减元素数量。高效的插入和删除操作&#xff0c;在任意位置插入和…

(四)进入MySQL 【事务】

一、MySQL事务的概念 MySQL 事务主要用于处理操作量大&#xff0c;复杂度高的数据。比如说&#xff0c;在人员管理系统中&#xff0c; 要删除一个人员&#xff0c;即需要删除人员的基本资料&#xff0c;又需要删除和该人员相关的信息&#xff0c;如信箱&#xff0c; 文章等等。…

unity中的InstanceID详解 即Object.GetInstanceID

GetInstanceID 是 Unity 中 Object 类的一个方法,它用于获取一个对象的唯一实例标识符。每个 Unity 对象(如游戏对象、组件、资源等)都有一个唯一的实例 ID,这个 ID 在对象的生命周期内是唯一的。 对于它的生命周期是不确定的。网上说在切换场景或者编辑器关闭重启后会变。…

红黑树刨析(删除部分)

文章目录 红黑树删除节点情景分析情景1&#xff1a;删除节点左右子树都为空情景1.1&#xff1a;删除节点为红色情景1.2&#xff1a;删除节点为黑色情况1.2.1&#xff1a;删除节点的兄弟节点是红色情景1.2.2&#xff1a;删除节点的兄弟节点是黑色情景1.2.2.1&#xff1a;删除节点…

计算机毕业设计选题推荐-大学生竞赛管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

初识Arduino

什么是Arduino Arduino是一款便捷灵活、方便上手的开源电子原型平台。它包含硬件部分&#xff08;即各种型号的Arduino板&#xff09;、软件部分&#xff08;即Arduino IDE&#xff09;&#xff0c;以及其Arduino社区平台。 Arduino由一个欧洲开发团队于2005年冬季开发&#…

56基于SpringBoot+Vue+uniapp的教学资源库的详细设计和实现(源码+lw+部署文档+讲解等)

文章目录 前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus 系统测试系统测试目的系统功能测试系统测试结论 为什么选择我代码参考数据库参考源码获取源码获取 前言 &#x1f31e;博主介绍 &#xff1a;✌全网粉丝15W,CSDN特邀作者、21…

信息学奥赛初赛天天练-80-NOIP2015普及组-基础题5-错位排列、二叉树、完全二叉树、叶子节点、完全二叉树叶子节点

NOIP 2015 普及组 基础题5 21 重新排列 1234使得每一个数字都不在原来的位置上&#xff0c;一共有( )种排法 22 一棵结点数为 2015的二叉树最多有( )个叶子结点 2 相关知识点 1) 错位排列 考虑一个有n个元素的排列&#xff0c;若一个排列中所有的元素都不在自己原来的位置…

建模杂谈系列250 Hello2Pymc

说明 pymc算是多年的老朋友了&#xff0c;中间失联了好几年。 内容 1 安装 安装更加麻烦了&#xff0c;不能很好的和其他的环境兼容。在官网上&#xff0c;也是建议用conda的方式安装。 conda create -c conda-forge -n pymc_env "pymc>5" conda activate p…

SQL基础——MySQL的优化

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正。 概述 在应用的的开发过程中&#xff0c;由于初期数据量小&#xff0c;开发人员写 SQL 语句时更重视功能上的实现&#xff0c;但是当应用系统正式上线后&#xff0c;随着生产数据量的急剧增长&…

安卓15发布日期确定,安卓15 谷歌GMS认证截止日期有重大变化!安卓版本GMS认证截止时间更新,谷歌GMS认证之MADA/EDLA设备认证截止时间介绍

谷歌正式公布安卓15发布日期&#xff0c;即9月3号&#xff0c;到时&#xff0c;安卓版本的认证时间将会有改变&#xff01;以下是深光标准整理的最新安卓版本的到期时间 详细讲解如何看懂这个图 第一列&#xff1a;OS version (API level) 指安卓版本 第二列&#xff1a;AOS…

软件测试工程师必备的技术能力

今年是我从事软件测试工作的第十年&#xff0c;从功能测试进阶到自动化测试&#xff0c;然后负责稳定性测试团队&#xff0c;进而兼任整个质量团队的技术专项治理&#xff0c;再到基础架构团队的测试专家角色&#xff0c;负责多个技术项目的产品/运营和质量保障工作。可以说绝大…

GNU 汇编语法基础

目录 一、引言 二、GNU 汇编基本结构 1.指令格式 2.注释 3. 段 三、寄存器和寻址方式 1.寄存器命名 2.寻址方式 四、指令集 1.数据传送指令 2.算术运算指令 3.逻辑运算指令 4.控制流指令 五、宏和函数 1.宏定义 2. 函数定义 六、总结 一、引言 在嵌入式系统…

南京观海微电子----VCC、 VDD、VSS、VEE 电压符号解释

一般在数据手册或者是说原理图中你会看到VCC、 VDD、VEE、VSS等不同的符号&#xff0c;那它们有什么区别&#xff0c;并且该怎么记住它们呢。 解释一&#xff1a; VCC&#xff1a;电源电压&#xff08;双极器件&#xff09;&#xff1b;电源电压&#xff08;74系列数字电路&a…