设计模式自学笔记001_Real(单例设计模式、工厂设计模式)
一、设计模式
1、六大原则
(1)单一原则:一个类或者一个方法只负责一项职责
(2)里氏替换原则:子类可以扩展父类的功能,但不能改变原来父类的功能
(3)依赖倒置原则:面向接口编程(通过接口作为参数实现)
1)抽象就是接口或者抽象类,两者依赖其抽象
2)上层模块不应该依赖下层模块,两者依赖抽象
3)抽象不应该依赖细节,尽量使用抽象类,或者接口
(4)接口隔离原则:建立单一接口(扩展为类也是一种接口,一切皆为接口)
定义:客户端不应该依赖它不需要的接口,类之间依赖关系应该建立在最小的接口上
复杂的接口,根据业务分成多个简单接口。
接口的设计粒度越小,系统越灵活,但是灵活的同时结构复杂度提高,开发难度也将变大,维护性降低。
(5)迪米特原则:最少知道原则,尽量降低类与类之间的耦合,一个对象应该对其他对象有最少额了解
(6)开闭原则:对拓展开放,都修改闭合
2、设计模式的分类
二、单例设计模式
什么是单例设计模式:
单例设计模式就是一个类只有一个实例化对象,因为有的类创建和销毁对资源来说消耗不大,所以可以每次进行创建和销毁,但是有的类就十分复杂,结构可能比较庞大,而且这些类是可以进行复用的,如果每次都进行单独的创建,用完后在销毁,将造成资源浪费,所以就可以用单例设计模式,直接创建类进行重复利用,不用了再销毁,这样就可以避免资源浪费。
public class HungryMan {
private HungryMan(){} //构造器私有化,这样其他人就不能构建该类的实例化对象
private static HungryMan instance = null;
public static HungryMan getInstance(){
if(instance == null){
instance = new HungryMan();
}
return instance;
}
}
但是这样有一个问题:就是再多线程情况下,就可能会出现多个线程同时进入,这样就会实例化很多次,那么久有一个解决方案就是加上synchronized关键字,将线程同步,如下:
public class HungryMan {
private HungryMan(){} //构造器私有化,这样其他人就不能构建该类的实例化对象
private static HungryMan instance = null;
public static synchronized HungryMan getInstance(){
if(instance == null){
instance = new HungryMan();
}
return instance;
}
}
但是这样每次调用这个getInstance()方法的时候都会进行同步线程的操作,所以也会造成资源浪费,那么我们只要在类加载额时候,在编译期就进行类的实例化,然后在运行时调用就可以避免了。
public class HungryMan {
private HungryMan(){} //构造器私有化,这样其他人就不能构建该类的实例化对象
private static HungryMan instance = new HungryMan();
public static HungryMan getInstance(){
return instance;
}
}
但是这样又会出现问题,这样会就会导致在不能在需要的时候进行实例化对象,而是直接在程序执行之前就实例化对象,然后在需要的时候调用,这样如果在程序运行之前就实例化太多不需要的对象也会造成一定的负担。那么我们回到第二个代码,只要我们在还没有实例化对象的时候加上锁让线程进行排队,然后实例化对象之后,就可以跳过实例化对象的步骤直接返回对象,这样就可以避免同时实例化多个对象的问题。
public class HungryMan {
private HungryMan(){} //构造器私有化,这样其他人就不能构建该类的实例化对象
private static HungryMan instance = null;
public static synchronized HungryMan getInstance(){
if(instance == null){
synchronized (HungryMan.class){
instance = new HungryMan();
}
}
return instance;
}
}
但是这样还会出现问题:如果同时多个线程检测道instance为空,那么就会有多个线程进入if语句块中,然后有一个线程获取锁进行实例化对象,然后其他线程会进行等待,当获取锁的线程执行完之后释放锁,其他线程抢到锁之后又会实例化对象,这又会造成多次对象实例化,造成负担,所以需要再加一个if判空的判断语句,这种方式因为有两个判空语句,所以叫做双检锁
public class HungryMan {
private HungryMan(){} //构造器私有化,这样其他人就不能构建该类的实例化对象
private static HungryMan instance = null;
public static synchronized HungryMan getInstance(){
if(instance == null){
synchronized (HungryMan.class){
if(instance == null){
instance = new HungryMan();
}
}
}
return instance;
}
}
但是这样还是会出现问题,这涉及到java的happen-before原则,一共八条,这里涉及到一个指令重排问题,只需要知道再程序中需要加volatile关键字就可以避免指令重排问题,下面就是完整的代码:
public class HungryMan {
private HungryMan(){} //构造器私有化,这样其他人就不能构建该类的实例化对象
private volatile static HungryMan instance = null;
public static synchronized HungryMan getInstance(){
if(instance == null){
synchronized (HungryMan.class){
if(instance == null){
instance = new HungryMan();
}
}
}
return instance;
}
}
但是在这样写代码比较复杂,还有一种比较简单的单例设计模式代码:利用了java的静态内部类,静态内部类再冲虚启动的时候不回加载,只有在第一次实例化对象的时候才会加载。
public class HungryMan {
private static class HungryHolder{
private static final HungryMan INSTANCE = new HungryMan();
}
private HungryMan(){}
public static final HungryMan getInstance(){
return HungryHolder.INSTANCE;
}
}
二、工厂设计模式
工厂设计模式广泛的讲就是创建一个工厂类,里面有各种各样的其他的类,我们会根据需求,利用工厂类里面的方法来返回我们所需要的其他类的实例化对象。
1、简单工厂设计模式
假如,目前有一个抽象类,他被很多类继承,子类重写了父类(该抽象类)的方法,那么我们就可以建一个工厂类,然后利用工厂类创建一个方法,每一个子类对应一个参数,我们输入参数,那么该工厂类的方法就会返回一个对应子类的实例化对象给我们使用,这就是简单工厂设计模式,我们用代码简单实现一下:
public class Factory01 {
public static Product getProduct(int flag) {
Product product = null;
if(flag == 1){
product = new Product1();
}else{
product = new Product2();
}
return product;
}
public static void main(String[] args) {
Product product = Factory01.getProduct(1);
product.PrintWord();
product = Factory01.getProduct(2);
product.PrintWord();
}
}
abstract class Product{
public void PrintWord(){}
}
class Product1 extends Product{
@Override
public void PrintWord() {
System.out.println("产品1");
}
}
class Product2 extends Product{
@Override
public void PrintWord() {
System.out.println("产品2");
}
}
我们通过传入一个参数给工厂,他就可以根据参数传给我们一个需要的实例化对象
优点就是:实现对象的创建和对象的使用分离,创建完全交给工厂区负责,程序员不需要关心这个类是怎么创建出来的,只需要关心怎么使用就可以了。但是同样也会有缺点,缺点就是这种设计模式不够灵活,如果有心的子类继承父类,那么就需要再工厂类中的对应方法进行修改与添加相应的代码实现。
当然也可以讲抽象类换成接口,然后其他类实现该接口,也可以实现简单工厂设计模式
2、工厂方法设计模式
简单工厂设计模式具有一个非常大的缺点就是没有遵循开闭原则,开闭原则就是对拓展开放,对修改关闭,而简单工厂设计模式他只有一个工厂,如果有新的产品类加进来就需要去该工厂类里面添加和修改相应的代码,这在程序设计中是非常危险的。
所以工厂方法设计模式就应运而生,他首先建立一个抽象工厂类或者接口类,里面有生产产品的抽象方法,然后每一个产品对应一个具体工厂类,具体工厂类负责创建与之对应的产品,并且这些具体工厂类重写抽象工厂类的方法,或者实现工厂接口的方法,这样以来,就可以不需要每次都去修改工厂类的代码,而是有新产品类加进来的时候直接新建新的具体工厂类来重写抽象工厂类的方法来创建产品即可,就不用再修改工厂类的代码了,这样就遵循了开闭原则,即不需要修改代码,但是对新拓展的产品时开放的。下面是一个例子:
java中的源代码也有许多利用工厂方法设计模式,典型的一个是Collection接口
我们将单例设计模式与工厂方法设计模式结合起来,就是以下代码:
public class Factory02 {
public static void main(String[] args) {
Factory factory1 = new Factory1();
Product product1 = factory1.CreateProduct();
product1.print();
Factory factory2 = new Factory2();
Product product2 = factory2.CreateProduct();
product2.print();
}
}
//抽象产品类
abstract class Product{
public void print(){}
}
//产品1类
class Product1 extends Product{
@Override
public void print() {
System.out.println("产品1");
}
}
//产品2类
class Product2 extends Product{
@Override
public void print() {
System.out.println("产品2");
}
}
//抽象工厂类
abstract class Factory{
public synchronized Product CreateProduct(){
return null;
}
}
//生产产品1的工厂1类
class Factory1 extends Factory{
public Factory1(){}
private volatile static Product instance = null;
@Override
public synchronized Product CreateProduct() {
//利用单例设计模式
if(instance == null){
synchronized (Product.class){
if(instance == null){
instance = new Product1();
}
}
}
return instance;
}
}
//生产产品2的工厂2类
class Factory2 extends Factory{
public Factory2(){}
private volatile static Product instance = null;
@Override
public synchronized Product CreateProduct() {
//利用单例设计模式
if(instance == null){
synchronized (Product.class){
if(instance == null){
instance = new Product2();
}
}
}
return instance;
}
}
工厂方法设计模式很好的解决了简单生产模式出现的问题,即没有遵循开闭原则(对拓展开放,对修改关闭),但是依然有不足之处,第一点就是这会让我们的程序结构变得十分复杂,代码量会增加很多,第二点就是一个工厂类只能生产一种类型的产品,即只能创建一个抽象类的子类的实例化对象,对于其他的抽象类的子类就不能创建相应的实例化对象了,那么就出现了抽象工厂设计模式。
3、抽象工厂设计模式
工厂方法设计模式是非常好的解决简单工厂设计模式没有遵循开闭原则的问题,而抽象方法解决的是工厂方法设计模式只能创建一种抽象类的子类的实例化对象的问题,它可以创建不同抽象类的子类的实例化对象。
那么要想让一个工厂类可以创建生成两种不同的产品类,那么直接的做法就是再工厂类中添加对应的创建其他产品的方法即可,比如一开始只有一个创建手机类的方法,然而现在希望这个工厂类还能创建生成口罩类,那么直接再抽象工厂类中添加创建口罩类的方法即可,然后让具体工厂类重写该方法即可。
举一个例子:比如现在工厂可以生产手机,有华为手机和苹果手机,那么疫情来了,老板看见商机,再工厂中引入了口罩的生产线,可以生产N95口罩和普通医用口罩,代码实现如下:
public class Factory03 {
public static void main(String[] args) {
Factory factory = new Factory1();
Phone iphone = factory.CreatePhone();
Mask mask = factory.CreateMask();
iphone.print();
mask.create();
factory = new Factory2();
iphone = factory.CreatePhone();
mask = factory.CreateMask();
iphone.print();
mask.create();
}
}
//抽象手机类
abstract class Phone {
public void print(){}
}
//抽象口罩类
abstract class Mask{
public void create(){}
}
//华为手机类
class HuaWei extends Phone {
@Override
public void print() {
System.out.println("生产了一个华为手机");
}
}
//苹果手机类
class Apple extends Phone {
@Override
public void print() {
System.out.println("生产了一个苹果手机");
}
}
//N95口罩类
class N95 extends Mask{
@Override
public void create() {
System.out.println("生产了一个N95口罩");
}
}
//普通口罩类
class OrdinaryMask extends Mask{
@Override
public void create() {
System.out.println("生产了一个普通口罩");
}
}
//抽象工厂类
abstract class Factory{
public synchronized Phone CreatePhone(){
return null;
}
public synchronized Mask CreateMask(){
return null;
}
}
//生产华为手机和N95口罩的工厂2类
class Factory1 extends Factory{
private volatile static Phone instance = null;
private volatile static Mask instance1 = null;
@Override
public synchronized Phone CreatePhone() {
//利用单例设计模式
if(instance == null){
synchronized (Phone.class){
if(instance == null){
instance = new HuaWei();
}
}
}
return instance;
}
@Override
public synchronized Mask CreateMask() {
//利用单例设计模式
if(instance1 == null){
synchronized (Mask.class){
if(instance1 == null){
instance1 = new N95();
}
}
}
return instance1;
}
}
//生产苹果手机和普通口罩的工厂类
class Factory2 extends Factory{
private volatile static Phone instance = null;
private volatile static Mask instance1 = null;
@Override
public synchronized Phone CreatePhone() {
//利用单例设计模式
if(instance == null){
synchronized (Phone.class){
if(instance == null){
instance = new Apple();
}
}
}
return instance;
}
@Override
public synchronized Mask CreateMask() {
//利用单例设计模式
if(instance1 == null){
synchronized (Mask.class){
if(instance1 == null){
instance1 = new OrdinaryMask();
}
}
}
return instance1;
}
}
这样,一个工厂类就可以创建多种不同类型的实例化对象,十一一个工厂类多种用途,但是抽象工厂设计模式也同样有缺点:那就是如果之后又需要这个工厂能够创建其他类型的实例化对象,那么就必须去修改工厂类的代码,而这样就不遵循开闭原则了。
所以,设计模式只是给我们提供一种思路,在工作中应该具体情况具体分析,找到一种适合本项目的设计模式,或者多种设计模式相互组合,并不是说哪一种设计模式最好,而是不同的设计模式适合不同的项目,应该具体情况具体分析。