面经系列之 设计模式(创建型)

创建型模式

1 简单工厂模式(普通简单工厂,多方法简单工厂,静态简单工厂)

在这里插入图片描述

1)模式定义

在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

2)类图

在这里插入图片描述

3)代码示例

1 鼠标信息接口

 public class LenoveMouse : IMouse
    {
        public string GetMouseInfo()
        {
            return "联想品牌鼠标";
        }
    }

2 联想鼠标实现类

 public string GetMouseInfo()
        {
            return "联想品牌鼠标";
        }

3 惠普鼠标实现类

public class HPMouse : IMouse
   {
       public string GetMouseInfo()
       {
           return "惠普品牌鼠标";
       }
   }

4 鼠标工厂类

public class MouseFactory
    {
        /// <param name="type">0 代码惠普鼠标 1 代码联想鼠标</param>
        /// <returns></returns>
        public IMouse GetMouse(int type)
        {
            if (type == 0)
            {
                return new HPMouse();
            }
            else if (type == 1)
            {
                return new LenoveMouse();
            }
            else {

                Console.WriteLine("请输入正确类型!");
                return null;
            }
        }
    }

5 控制台调用

class Program
    {
        static void Main(string[] args)
        {
            MouseFactory mouseFactory = new MouseFactory();
           string mouseInfo1 = mouseFactory.GetMouse(0).GetMouseInfo();
            Console.WriteLine(mouseInfo1);
           // 输出:惠普品牌鼠标
            string mouseInfo2 = mouseFactory.GetMouse(1).GetMouseInfo();
            Console.WriteLine(mouseInfo2);
           //输出:联想品牌鼠标
        }
    }

多方法简单工厂
是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的类型出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象.

工厂类修改:

public class MouseFactory
    {

        public IMouse GetHPMouse()
        {
            return new HPMouse();
        }

        public IMouse GetMouse()
        {
            return new LenoveMouse();
        }

    }

控制台调用修改:

 class Program
    {
        static void Main(string[] args)
        {
            MouseFactory mouseFactory = new MouseFactory();
            string mouseInfo1 = mouseFactory.GetHPMouse().GetMouseInfo();
            //输出惠普鼠标品牌
            Console.WriteLine(mouseInfo1);          
            string mouseInfo2 = mouseFactory.GetLenoveMouse().GetMouseInfo();
            //输出联想鼠标品牌
            Console.WriteLine(mouseInfo2);
            Console.ReadLine();
        }
    }

静态方法简单工厂

将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

 public class MouseFactory
    {

        public static IMouse GetHPMouse()
        {
            return new HPMouse();
        }

        public static IMouse GetLenoveMouse()
        {
            return new LenoveMouse();
        }

    }

控制台调用:

 class Program
    {
        static void Main(string[] args)
        {          
            string mouseInfo1 = MouseFactory.GetHPMouse().GetMouseInfo();
            //输出惠普鼠标品牌
            Console.WriteLine(mouseInfo1);          
            string mouseInfo2 = MouseFactory.GetLenoveMouse().GetMouseInfo();
            //输出联想鼠标品牌
            Console.WriteLine(mouseInfo2);
            Console.ReadLine();
        }
    }

4)优缺点

优点:
工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点:
由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
使用简单工厂模式将会增加系统中类的个数,在一定程序上增加了系统的复杂度和理解难度。
系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

5)适用场景

在以下情况下可以使用简单工厂模式:

工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
客户端只知道传入工厂类的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。

2 工厂方法模式

1)模式定义

简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改。假如增加其他品牌鼠标,工厂类需要修改,如何解决?就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

2)类图

在这里插入图片描述

3)代码示例

增加Factory接口:

 public interface Factory
    {
        /// <summary>
        /// 获取鼠标工厂
        /// </summary>
        /// <returns></returns>
         IMouse GetMouseFactory();

    }

添加惠普鼠标工厂实现类:

public class HPMouseFactory:Factory
    {
        public IMouse GetMouseFactory()
        {
            return new HPMouse();
        }
    }

添加联想鼠标工厂实现类:

 public class LenoveMouseFactory : Factory
    {
        public IMouse GetMouseFactory()
        {
            return new LenoveMouse();
        }
    }

控制台调用:

class Program
{
    static void Main(string[] args)
    {
        //实例化惠普鼠标工厂
        Factory factory = new HPMouseFactory();
        string mouseInfo1 = factory.GetMouseFactory().GetMouseInfo();
        //输出惠普鼠标品牌
        Console.WriteLine(mouseInfo1);
        //实例化联想鼠标工厂
        Factory factory2 = new LenoveMouseFactory();
        string mouseInfo2 = factory2.GetMouseFactory().GetMouseInfo();
        //输出联想鼠标品牌
        Console.WriteLine(mouseInfo2);
        Console.ReadLine();
    }
}

4)优缺点

优点:
在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
缺点:
在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

工厂方法模式中我们把生成产品类的时间延迟,就是通过对应的工厂类来生成对应的产品类,在这里我们就可以实现“开发-封闭”原则,无论加多少产品类,我们都不用修改原来类中的代码,而是通过增加工厂类来实现。
但是这还是有缺点的,如果添加键盘产品,就需要添加键盘工厂类。假如我们要实现的产品接口不止一个,也就是有多个产品接口,不同产品接口有对应的产品族。什么是产品族呢?简单的理解就是,不同厂家的不仅有鼠标,还有键盘,音响,笔记本可以组成一个产品族。对于这种情况我们可以采用抽象工厂模式。

5)适用场景

一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。

3 抽象工厂模式

1)模式定义

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。这时候就需要用到抽象工厂。

2)类图

在这里插入图片描述

3)代码示例

创建工厂接口类提供获取鼠标鼠标和键盘接口:

 public interface Factory
    {
        /// <summary>
        /// 获取鼠标工厂
        /// </summary>
        /// <returns></returns>
         IMouse GetMouseFactory();
        /// <summary>
        /// 获取键盘工厂
        /// </summary>
        /// <returns></returns>
        IKeyboard GetKeyboardFactory();

    }

创建联想工厂实现类:

public class LenoveFactory : Factory
    {
        public IKeyboard GetKeyboardFactory()
        {
            return new LenoveKeyboard();
        }

        public IMouse GetMouseFactory()
        {
            return new LenoveMouse();
        }
    }

创建惠普工厂实现类型:

public class HPFactory:Factory
    {
        public IKeyboard GetKeyboardFactory()
        {
            return new HPKeyboard();
        }

        public IMouse GetMouseFactory()
        {
            return new HPMouse();
        }
    }

控制台:

 class Program
    {
        static void Main(string[] args)
        {
            //实例化惠普鼠标工厂
            Factory factory = new HPFactory();
            string mouseInfo1 = factory.GetMouseFactory().GetMouseInfo();
            string keyboard1 = factory.GetKeyboardFactory().GetKeyboardInfo();

            //输出惠普鼠标品牌
            Console.WriteLine(mouseInfo1);
            //输出惠普键盘
            Console.WriteLine(keyboard1);
            //实例化联想鼠标工厂
            Factory factory2 = new LenoveFactory();
            string mouseInfo2 = factory2.GetMouseFactory().GetMouseInfo();
            //输出联想鼠标品牌
            Console.WriteLine(mouseInfo2);
            //输出联想键盘品牌
            string keyboard2= factory2.GetKeyboardFactory().GetKeyboardInfo();
            Console.ReadLine();
        }
    }

4)优缺点

优点:
抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。

缺点:
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。

5)适用场景

在以下情况下可以使用抽象工厂模式:

一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
系统中有多于一个的产品族,而每次只使用其中某一产品族。
属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。

4 建造者模式

1)定义

将一个复杂对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示;用户只需指定需要建造的类型就可以得到它们(相同的构建过程创建出不同的产品),不需要知道建造的过程以及细节。

2)类图

在这里插入图片描述

3)代码示例

参考晔哥的代码

package builder;

/**
 * @author zzy
 * @create 2020-01-04 12:01
 */
public class Course {
    String name;
    String ppt;
    String video;

    public String getName() {
        return name;
    }

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

    public String getPpt() {
        return ppt;
    }

    public void setPpt(String ppt) {
        this.ppt = ppt;
    }

    public String getVideo() {
        return video;
    }

    public void setVideo(String video) {
        this.video = video;
    }


    @Override
    public String toString(){
        return this.name +this.ppt + this.video;
    }
}

package builder;

/**
 * @author zzy
 * @create 2020-01-04 12:07
 */
public abstract class Builder {
    public abstract void setCourseName(String name);

    public abstract void setCoursePpt(String ppt);

    public abstract void setCourseVideo(String video);

    public abstract Course makeCourse();
}

package builder;

/**
 * @author zzy
 * @create 2020-01-04 12:09
 */
public class ConcreteBuilder extends Builder {

    Course course = new Course();

    @Override
    public void setCourseName(String name) {
        course.setName(name);
    }

    @Override
    public void setCoursePpt(String ppt) {
        course.setPpt(ppt);
    }

    @Override
    public void setCourseVideo(String video) {
        course.setVideo(video);
    }


    @Override
    public Course makeCourse(){
        System.out.println(course.name + course.ppt + course.video);
        return course;
    }
}

package builder;

/**
 * @author zzy
 * @create 2020-01-04 12:15
 */
public class Coach {
    Builder builder;

    public void setBuilder(Builder builder){
        this.builder = builder;
    }

    public Course makeCourse(String name,String ppt,String video){

        builder.setCourseName(name);
        builder.setCoursePpt(ppt);
        builder.setCourseVideo(video);
        return builder.makeCourse();

    }

}

package builder;

/**
 * @author zzy
 * @create 2020-01-04 12:18
 */
public class Test {

    public static void main(String []args){
        Coach coach = new Coach();
        Builder builder = new ConcreteBuilder();
        coach.setBuilder(builder);
        coach.makeCourse("zzy","zzy","zzy");
        System.out.println();
    }
}

4)优缺点

优点:
在建造者模式中, 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
缺点:
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

5)适用场景

需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。
需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

建造者模式与抽象工厂模式的比较:

与抽象工厂模式相比, 建造者模式返回一个组装好的完整产品 ,而 抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族。
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象。
如果将抽象工厂模式看成 汽车配件生产工厂 ,生产一个产品族的产品,那么建造者模式就是一个 汽车组装工厂 ,通过对部件的组装可以返回一辆完整的汽车。

5 单例模式

1)定义

所谓单例,就是整个程序有且仅有一个实例。该类负责创建自己的对象,同时确保只有一个对象被创建。
类构造器私有
持有自己类型的属性
对外提供获取实例的静态方法

2)类图

晔哥的博客

3)代码示例(饿汉、懒汉模式)

1 懒汉
线程不安全、延迟初始化

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

2 饿汉
线程安全,比较常用,但是容易产生垃圾(因为一开始就初始化)

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

3 懒汉+双重检查

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

双重检查模式,进行了两次的判断,第一次是为了避免不要的实例,第二次是为了进行同步,避免多线程问题。由于singleton=new Singleton()对象的创建在JVM中可能会进行重排序在多线程访问下存在风险,使用volatile修饰signleton实例变量有效,解决该问题

4 静态内部类单例模式

public class Singleton { 
    private Singleton(){
    }
      public static Singleton getInstance(){  
        return Inner.instance;  
    }  
    private static class Inner {  
        private static final Singleton instance = new Singleton();  
    }  
} 

只有第一次调用getInstance方法时,虚拟机才加载 Inner 并初始化instance ,只有一个线程可以获得类的初始化锁,其他线程无法进行初始化,保证对象的唯一性。目前此方式是所有单例模式中最推荐的模式。

众所周知,单例模式是创建型模式,都会新建一个实例。那么一个重要的问题就是反序列化。当实例被写入到文件到反序列化成实例时,我们需要重写readResolve方法,以让实例唯一。

private Object readResolve() throws ObjectStreamException{
        return singleton;
}

4)优缺点

优点:

提供了对唯一实例的受控访问。因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它,并为设计及开发团队提供了共享的概念。
由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式无疑可以提高系统的性能。
允许可变数目的实例。我们可以基于单例模式进行扩展,使用与单例控制相似的方法来获得指定个数的对象实例。
缺点:

由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
单例类的职责过重,在一定程度上违背了“单一职责原则”。因为单例类既充当了工厂角色,提供了工厂方法,同时又充当了产品角色,包含一些业务方法,将产品的创建和产品的本身的功能融合到一起。
滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;现在很多面向对象语言(如Java、C#)的运行环境都提供了自动垃圾回收的技术,因此,如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失。

5)适用场景

系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
在一个系统中要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就需要对单例模式进行改进,使之成为多例模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值