设计模式(二)

创建性模式

用于描述如何创建对象,主要特点将对象的创建与使用分离。

创建对象时,不再由我们直接实例化对象,而是根据特定的场景,由程序来确定创建对象的方式,从而保证更大的性能,更好的架构优势。

与一个对象相关的职责通常有三类:对象本身所具有的职责、创建对象的职责和使用对象的职责。两个类A和B之间的关系应该仅仅是A创建B或是A使用B,而不能两种关系都有。将对象的创建和使用分离,也使得系统更加符合“单一职责原则”。防止用来实例化一个类的数据和代码在多个类中到处都是,可以将有关创建的知识搬移到一个工厂类中,对于有多个构造方法的,可以提供一系列名字完全不同的工厂方法,每一个工厂方法对应一个构造函数,如果产品类很简单,而且不存在太多变数,其构造过程也很简单,此时无须为其提供工厂类。对于抽象类或者接口可以通过工厂的方法创建子类,或者是通过工厂来获取其他的产品类,可以是毫无关系的,但一般都是兄弟类。如果不用工厂直接自己去new,那么类就只能用该父类底下的这一个子类,当想要用其他子类就需要改源代码。

 

1、简单工厂

定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法,因此简单工厂模式又被称为静态工厂方法模式。

它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节不灵活,对于新添加的子类就需要去修改工厂的创建的实例。首先应该创建和使用分离,不要都放在一个类中,耦合度太高,一个类职责太多。使用工厂类来负责创建对象,在工厂方法中,通过传入参数来标识获取哪个对象,对象中有不同的构造方法,每个构造方法在工厂中都可以去创建一个。对于工厂类,用户无需知道细节,只要知道传什么参数就行,连创建的类名也不用知道。也可以创建各种各样的抽象类的方法,一个方法对应创建一类产品,通过传入参数来实现抽象类具体化,再把该抽象类返回出来。不能设计一个包罗万象的产品类,而需根据实际情况设计一个产品层次结构。对于获取的对象是通过传入参数来实现,当需要改变获取其他对象的时候只需要去修改参数就行,可以通过配置文件来读取这里面的参数来实例化对象,这样就不要去修改源代码来修改,修改配置文件就行。但对于需要去添加新的对象还得去修改源代码。可以通过反射来创建实体类,这样需要新添加的子类也不用去修改源代码,但是都需要知道类的名字,可以通过工厂方法模式。


适用场景:工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂,客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

 

2、工厂方法

不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类。使用简单工厂的时候,获取不同具体类对象得去判断符合哪个,如果有多个对象的话就要进行多个判断,对于增加新类型的日志记录器,必须修改静态工厂方法的业务逻辑。我们可以对工厂类再进行抽象一层,抽象工厂中声明了工厂方法但并未实现工厂方法,具体产品对象的创建由其子类负责,子类对应各个具体工厂类,具体工厂类实现了工厂方法来获取具体对象,返回该具体对象的抽象类,后序去使用该类会更方便。具体工厂类在实现工厂方法时除了创建具体产品对象之外,还可以负责产品对象的初始化工作以及一些资源和环境配置工作,例如使用不同的数据库。但是我们得需知道具体工厂类的类名,要去new一个实际的工厂类,后面要更改工厂类就要去修改源代码,我们可以通过配置文件和反射来实现,配置各种具体工厂的类名,通过反射去加载实例化,要更改也只要更该配置文件。对于要新添加的对象,可以通过创建一个工厂类然后实现抽象工厂接口,就能创建一个新的对象,而不用去修改源代码。
在抽象工厂中定义多个重载的工厂方法,在具体工厂中实现了这些工厂方法,这些方法可以包含不同的业务逻辑,以满足对不同产品对象的需求,同一个对象可以在创建的时候对于不同的初始化可以对应一个具体工厂类的方法,也可以通过具体工厂的不同方法使用同一个对象调用不同方法,直接在工厂类调用产品类的业务方法。


适用场景:客户端不知道它所需要的对象的类,抽象工厂类通过其子类来指定创建哪个对象。

 

3、抽象工厂

将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产。抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类。
当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率。先定义一个抽象工厂,该工厂的产品由其他产品组建而成,那么具体工厂并只要去创建只需要组建该产品的对象,而不需要像工厂方法一样每一个组建该厂品的又都一一对应一个工厂类。
在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,增加一个产品族只要去实现接口而无需修改源代码,但增加新的产品等级结构,比如该厂品去增加一个零件,那么就得在抽象工厂中增加创建该零件得方法,具体工厂去实现方法new一个新对象,那么就要对源代码修改了。


适用场景:一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,统中有多于一个的产品族,而每次只使用其中某一产品族,属于同一个产品族的产品将在一起使用。

 

4、单例模式

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例
懒汉式使用延迟加载技术会在多线程中出现多例,在getInstance()方法上加synchronized会影响高并发,在if判断中使用synchronized锁住类,在多线程中可能出现有线程同时进入if判断,而其中一个等待阻塞中,另一个正在创建,当释放锁的时候,等待的线程又会再创建一个,所以还需要在加一个判断,并且该变量要用volatile修饰。由于volatile关键字会屏蔽Java虚拟机所做的一些代码优化,可能会导致系统运行效率降低。而使用饿汉式在类被加载时就将自己实例化,无论是否需要这对象都会创建,如果该对象初始化很慢就会导致程序刚开始运行会很慢。所以我们可以将两者合二为一使用静态内部类,在内部类中使用成员变量new一个对象,外部类用方法引用该变量,静态单例对象没有作为外部类的成员变量直接实例化,在使用静态内部类的时候就会加载该内部类,因为它的成员变量是静态的,所以也会一起实例化。单例类的职责过重,自己创建了对象。


适用场景:系统只需要一个实例对象,客户调用类的单个实例只允许使用一个公共访问点。

 

5、原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。可以使用clone接口来实现,或者序列化流来实现。


适用场景:创建新对象成本较大,如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。

 

6、建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式为客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品,复杂对象是指那些包含多个成员属性的对象。
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。可以通过添加指挥者来使用建造者,并可以按一定顺序创建对象,在指挥者内部还可以加具体逻辑,来判断指定需不需要该组件。
建造者模式与抽象工厂模式有点相似,但是建造者模式返回一个完整的复杂产品,而抽象工厂模式返回一系列相关的产品。抽象工厂模式是把各种厂品放一起搭配成一个产品族,而建造者是一个大的厂品由很多小厂品组成。抽象工厂模式看成一个汽车配件生产厂,生成不同类型的汽车配件,那么建造者模式就是一个汽车组装厂,通过对配件进行组装返回一辆完整的汽车。建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,就不适合使用建造者模式。


适用场景:需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性,需要生成的产品对象的属性相互依赖,需要指定其生成顺序。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值