https://blog.csdn.net/zhuralll112/article/details/89360277
#工厂模式
单例设计模式的关键点
一.私有构造函数
二.声明静态单例对象
三.构造单例对象之前要加锁(lock一个静态的object对象,某些语言可以声明同步执行,其实是一个目的)
四.需要两次检测单例实例是否已经被构造,分别在锁之前和锁之后
好了,本文将向大家来讨论一下工厂模式,并且归纳工厂模式的关键点。
##分类
一般分为3种。简单工厂,工厂和抽象工厂**
##本质
工厂模式的核心思想,就是解耦“需求”“工厂”和“产品”
##工厂模式的作用
1 解耦,工厂方法封装了对象创建的细节,将对象创建代码和其他部分脱离,减少相干性.
2 有利于同类对象创建的统一管理和控制
3 更好的扩展性,减少修改。你所关心的仅仅是工厂方法返回的接口方法,不必关心实现细节
####理解:
一般 类的构造函数,new实例,Sample sample=new Sample(参数);
但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了
初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我**们,尽量将长的代码分派“切割”成每段,将每段再“封装 ”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。
我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。
你想如果有多个类似的类,我们就需要实例化出来多个类。这样代码管理起来就太复杂了。
这个时候你就可以采用工厂方法来封装这个问题。
我在面试的时候,有时候会问到候选人有没有熟悉的设计模式,一般大部分候选人会选择说熟悉单例和工厂(也有部分人会说熟悉观察者),如果我进一步问候选人是如何应用工厂模式的,10个候选人有10个会举连接Sqlserver,Oracle,Mysql等不同数据库时会用工厂模式产生不同的连接的例子。(举个更实际的例子,比如你写了个应用,里面用到了数据库的封装,你的应用可以今后需要在不同的数据库环境下运行,可能是oracle,db2,sql server等,那么连接数据库的代码是不一样的,你用传统的方法,就不得不进行代码修改来适应不同的环境,非常麻烦,但是如果你采用工厂类的话,将各种可能的数据库连接全部实现在工厂类里面,通过你配置文件的修改来达到连接的是不同的数据库,那么你今后做迁移的时候代码就不用进行修改了。
我通常都是用xml的配置文件配置许多类型的数据库连接,非常的方便。PS:工厂模式在这方面的使用较多。)
OMG,我知道大家都是爱学习的同学,但是大家为什么不仔细想想,网上一搜一大堆的例子,你能看到,别人难道看不到?我不知道大家能不能感受,作为一个面试官,问到一个问题的时候,所有候选人都用同一个例子来回答你的感受。。。
如果我进一步问,工厂模式中,简单工厂,工厂和抽象工厂的区别,以及每种工厂的优劣势时,候选人基本都会蒙圈。如果自己没有使用过,甚至使用过没有认真思考过,肯定是回答不上来的。好吧,下面我就来跟大家讲讲工厂模式。
工厂模式理论我不长篇大论了。其核心功能是根据“需求”生产“产品”,还记得我上一篇说的,设计模式的核心是解耦吗?工厂模式就是为了解耦“需求”和“产品”,但是别忘了,工厂模式工厂模式,还有一个重要元素,就是“工厂”,所以工厂模式的核心思想,就是解耦“需求”“工厂”和“产品”。
工厂模式,实际上也会根据业务情景不同会有不同的实现方式。一般分为3种。简单工厂,工厂和抽象工厂。顾名思义,这三种从简单到抽象,名称越来越高大上,实现方式肯定是越来越复杂,所以,我们可以得到第一个结论,三种工厂的实现是越来越复杂的。
先来看看简单工厂。废话不多,撸代码:
public class SimpleFactory {
int prodNo;
public SimpleFactory(int prodNo) //构造工厂时告知工厂产品标识
{
this.prodNo = prodNo;
}
public IProduct GetProduct()
{
switch (prodNo) //根据产品标识生产产品
{
default:
return new ProductA();
case 1:
return new ProductA();
case 2:
return new ProductB();
}
}
}
//产品A
class ProductA: IProduct
{
//产品属性
//......
}
//产品B
class ProductB : IProduct
{
//产品属性
//......
}
//产品接口
interface IProduct
{
//产品方法
//......
}
注意,这段例子代码当然还可以写的简单点,我完全可以在简单工厂中直接返回字符串而避免写产品类和产品接口。但是即便是真实的业务场景是这样(真的只需要返回字符串或者数字什么的),大家还是把产品类和工厂类分开,这样才是使用工厂模式的初衷,也就是实现解耦。
那么大家看看这段简单工厂的例子,如果我现在问,这个会有什么问题,该如何回答呢?提示一下,如果说来了一个需求,增加一个产品C,该如何办?没错,简单工厂的问题就在于swich case(或者if else)。每当新增一种产品时,你都需要去维护工厂中的判断语句,造成的后果就是可能这个工厂类会非常非常长,各种判断全部挤在一起,给扩展和维护带来很多麻烦。说白了,你的产品和工厂还是没有完全解耦,绑定在一起的。所以,我们得到了第二个结论:简单工厂通过构造时传入的标识来生产产品,不同产品都在同一个工厂中生产,这种判断会随着产品的增加而增加,给扩展和维护带来麻烦。那么,如何解决这个问题呢?
你猜对了,工厂模式可以解决这个问题,代码撸上:
interface IFactory //工厂接口
{
IProduct GetProduct();
}
//A工厂类
public class FactoryA: IFactory
{
IProduct productA;
public FactoryA()
{
this.productA = new ProductA();
}
public IProduct GetProduct() //A工厂生产A产品
{
return this.productA;
}
}
//B工厂类
public class FactoryB : IFactory
{
IProduct productB;
public FactoryB()
{
this.productB = new ProductB();
}
public IProduct GetProduct() //B工厂生产B产品
{
return this.productB;
}
}
//产品接口
public interface IProduct
{
//产品方法
//......
}
//产品A
public class ProductA : IProduct
{
//产品属性
//......
}
//产品B
public class ProductB : IProduct
{
//产品属性
//......
}
仔细观察这段代码,在工厂模式中,已经将工厂类分开,不再将所有产品在同一工厂中生产,这样就解决了简单工厂中不停的switch case的问题。如果说来了一个C产品,那么我们只需要写一个C工厂和C产品,在调用时用C工厂生产C产品即可,A和B工厂和产品完全不受影响。OK,优化说完了,但是还是有问题。
问题在哪里呢?当业务需求是需要生产产品族的时候,工厂就不再适合了。首先我们搞清楚何谓产品族和产品等级结构。举个例子来说,比如三星是一个品牌,三星生产洗衣机,电视,冰箱;格力也是一个品牌,格力也生产洗衣机,电视,冰箱。那么,三星工厂和格力工厂生产的2个品牌的洗衣机,就在洗衣机这种产品的产品等级结构中(当然洗衣机产品等级结构中还有LG,海尔,三菱等等不同的品牌的工厂的产品),所以,洗衣机就是一个产品等级。那么三星生产的三星洗衣机,三星电视机,三星冰箱就是三星这个工厂的产品族。可能还会有西门子工厂产品族,格力工厂产品族,美的工厂产品族等等。
好了,搞清楚了产品等级结构和产品族,我们得到第三个结论:工厂模式无法解决产品族和产品等级结构的问题。再回过头来看抽象工厂模式。如果如上所述,业务场景是需要实现不同的产品族,并且实现产品等级结构,就要用到抽象工厂模式了。还是来看代码:
//工厂接口,即抽象工厂
interface IFactory
{
IFridge CreateFridge();
IAirCondition CreateAirCondition();
}
//三星的工厂,生产三星的产品族
public class SamsungFactory : IFactory
{
public IAirCondition CreateAirCondition()
{
return new SamsungAirCondition(); //三星的工厂生产三星的空调
}
public IFridge CreateFridge()
{
return new SamsungFridge(); //三星的工厂生产三星的冰箱
}
}
//格力的工厂,生产格力的产品族
public class GreeFactry : IFactory
{
public IAirCondition CreateAirCondition()
{
return new GreeAirCondition(); //格力的工厂生产格力的空调
}
public IFridge CreateFridge()
{
return new GreeFridge(); //格力的工厂生产格力的冰箱
}
}
//冰箱产品接口
public interface IFridge
{
//冰箱产品接口
//冰箱的action
}
//空调接口
public interface IAirCondition
{
//空调产品接口
//空调的action
}
//三星的冰箱
public class SamsungFridge: IFridge
{
//三星冰箱的action和property
}
//格力的冰箱
public class GreeFridge : IFridge
{
//格力冰箱的action和property
}
//三星的空调
public class SamsungAirCondition : IAirCondition
{
//三星空调的action和property
}
//格力的空调
public class GreeAirCondition : IAirCondition
{
//格力空调的action和property
}
我们可以看到,在工厂模式中,一个工厂生产一个产品,所有的具体产品是由同一个抽象产品派生的,不存在产品等级结构和产品族的概念;而在抽象工厂中,同一个等级的产品是派生于一个抽象产品(即产品接口),一个抽象工厂派生不同的具体工厂,每个具体工厂生产自己的产品族(包含不同产品等级)。所以我们得到第四个结论,工厂模式中,一个工厂生产一个产品,所有产品派生于同一个抽象产品(或产品接口);而抽象工厂模式中,一个工厂生产多个产品,它们是一个产品族,不同的产品族的产品派生于不同的抽象产品(或产品接口)。
好了我们归纳一下,工厂模式实际上包含了3中设计模式,简单工厂,工厂和抽象工厂,关键点如下:
一、三种工厂的实现是越来越复杂的
二、简单工厂通过构造时传入的标识来生产产品,不同产品都在同一个工厂中生产,这种判断会随着产品的增加而增加,给扩展和维护带来麻烦
三、工厂模式无法解决产品族和产品等级结构的问题
四、抽象工厂模式中,一个工厂生产多个产品,它们是一个产品族,不同的产品族的产品派生于不同的抽象产品(或产品接口)。
好了,如果你能理解上面的关键点,说明你对工厂模式已经理解的很好了,基本上面试官问你工厂模式,你可以昂头挺胸的说一番。但是,面试官怎么可能会放过每一次虐人的机会?你仍然可能面临下面的问题:
在上面的代码中,都使用了接口来表达抽象工厂或者抽象产品,那么可以用抽象类吗?有何区别?
从功能上说,完全可以,甚至可以用接口来定义行为,用抽象类来抽象属性。抽象类更加偏向于属性的抽象,而用接口更加偏向行为的规范与统一。使用接口有更好的可扩展性和可维护性,更加灵活实现松散耦合,所以编程原则中有一条是针对接口编程而不是针对类编程。
- 到底何时应该用工厂模式
根据具体业务需求。不要认为简单工厂是用switch case就觉得一无是处,也不要觉得抽象工厂比较高大上就到处套。我们使用设计模式是为了解决问题而不是炫技,所以根据三种工厂模式的特质,以及对未来扩展的预期,来确定使用哪种工厂模式。
3.说说你在项目中工厂模式的应用
如果你看了这篇文章,被问到这个问题时,还傻乎乎的去举数据库连接的例子,是要被打板子的。。。比如我之前做过一个旅游产品的B2B网站,根据不同类型的业务形态,产品也是不同的,有国内跟团,出境跟团,国内自由行,出境自由行,邮轮五种产品,并且后面可能还会有门票,酒店,机票等等产品,其中有些联系也有些区别。
所以在面试中,我完全把工厂模式和我做的东西联系起来,如何建立工厂,如何生产不同的产品,如何扩展,如何维护等等。我想,把理论应用到实际,而且是真实业务逻辑中,给面试官的印象无论如何不会太差,甚至会对你刮目相看。
当然,即便是你没有真的使用过,如果面试官问道了工厂模式,你仍然可以把你以往的经验和设计模式联系起来回(hu)答(you)面试官,只要你理解了,把来龙去脉说清楚,并且可以回答问题,我想应该是可以令面试官满意的。