⾯试常问的⼏⼤设计模式(⼀)
本博客内容:
单例模式的几种实现方式
⼀、单例模式
⼆、⼯⼚模式
三、观察者模式
装饰者模式
适配器模式
代理模式
设计模式不是⾼深技术,奇技淫巧,只是⼀种设计思想,针对不同的业务场景,最本质的⽬的是解耦,为了可扩展性和健壮性。
⼀、单例模式
class Singleton{
private:
Singleton();
static Singleton * m_singleton=NULL;
static objcect obj=new object();
public:
Singleton & getSingleton()
{
if(m_singleton==NULL)
{
lock(obj)
{
if(m_singleton==NULL)
m_singleton=new Singleton();
}
}
return m_singleton;
}
可能的问题:
1.为何要检测2次?
有可能延迟或者缓存原因,造成构造多个实例,违反了单例的初衷。
2.构造函数能否公有化
不,单例类的构造函数必须私有化,单例类不能被实例化,只能被静态调⽤。
3.lock住的对象为什么要是object对象,可以是int型吗?
不⾏,锁住的必须是个引⽤类型,如果锁值类型,每个不同的线程在声明的时候值类型变量的地址都不⼀样,那么上个线程锁住的东西,下个线程进来会认为根本没有锁,相当于每次都锁了不同的门。⽽引⽤类型的变量地址是相同的,每个线程进来判断锁都是判断同⼀个地址,相当于锁在同⼀扇门,起到了锁的作⽤。
⼆、⼯⼚模式
int prodNo;
class IProduct
{
public:
SimpleFactroy(int proNo)
{
this.prodNo=prodNo;
}
IProduct GetProduct()
{
switch(prodNo)
{
case1:
return new ProductA();
case2:
return new ProductB();
default:
return new ProductC();
}
}
}
//产品A
class ProductA: IProduct
{
//属性  ...
}
把产品类和⼯⼚类分开,才是使⽤⼯⼚模式的初衷。
简单⼯⼚模式存在的问题:
⽐如来了新需求,D、E、F、G、H等,需要再switch case或者 if else中维护⼯⼚中的判断语句,造成的后果就是可能这个⼯⼚类会⾮常⾮常长,各种判断全部挤在⼀起,给扩展和维护带来⿇烦,就是说,⼯⼚和产品还没有完全解耦,绑定在⼀起的。得出结论:
简单⼯⼚通过构造时传⼊的标识来⽣产产品,不同产品都在同⼀个⼯⼚中⽣产,会随着产品的增加⽽增加,给扩展和维护带来⿇烦。
引出⼯⼚模式:
interface  IFactroy  //⼯⼚接⼝
{
IProduct GetProduct();
}
//A⼯⼚类
class FactroyA:IFactroy
{
IProduct productA;
public:
FactroyA()
{
this.productA=new ProductA();
}
IProduct GetProduct()
{
return this.productA;    //A⼯⼚⽣产A产品
}
}
...B同理          //B⼯⼚⽣产B产品
//产品A
class ProductA: IProduct
{
//属性...
}
...其他产品
在上述代码中,已将⼯⼚类分开,不再将所有产品都在同⼀⼯⼚中⽣产,解决了简单⼯⼚中不停的switch case问题。如果来了⼀个C产品,我们只需要写⼀个C⼯⼚,⼀个C产品,在调⽤时⽤C⼯⼚⽣产C产品即可,即A和B⼯⼚和产品完全不受影响。
仍存在问题:
当业务需求是需要⽣产产品族的时候,⼯⼚就不再适合了,何谓产品族和产品等级结构?举例:
三星的洗⾐机、电视机、冰箱就是三星这个⼯⼚的产品族。
产品等级结构:例如洗⾐机的等级结构中有三星的、四门⼦的、LG、海尔、三菱等等
引出结论三:⼯⼚模式⽆法解决产品族和产品等级结构的问题
//⼯⼚接⼝,即抽象⼯⼚
interface IFcatroy
{
IFridge CreateFridge();
IAirCondition CreateAirCondition();
}
//三星的⼯⼚,⽣产三星的产品族
class SamsungFactory: IFactroy
{
public :
IAirCondition CreateAirCondition()
{
return new SamsungAirCondiction();//三星的⼯⼚⽣产三星的空调
}
IFridge CreateFridge()
{
return new SamsungFridge(); //三星的⼯⼚⽣产三星的冰箱
}
}
//类似以上,格⼒也有⾃⼰的⼯⼚,⽣产格⼒的产品族
//接下来
//冰箱产品接⼝
interface IFridge
{
//冰箱产品接⼝,action
}
interface IAirCondition
{
//空调产品接⼝,action
}
class SamsungFridge:IFridge
{
//三星的冰箱...
}
//格⼒的冰箱...
可以看出,在⼯⼚模式中,⼀个⼯⼚⽣产⼀个产品,所有的具体产品都是由同⼀个抽象产品派⽣来的,不存在产品等级结构和产品族的概念;⽽在抽象⼯⼚中,同⼀个等级的产品是派⽣于⼀个抽象产品(即产品接⼝),⼀个抽象⼯⼚派⽣不同的具体⼯⼚,每个具体⼯⼚⽣产⾃⼰的产品族(包含不同产品等级)。
得出结论:
⼯⼚模式中,⼀个⼯⼚⽣产⼀个产品,所有产品派⽣于同⼀个抽象产品(或产品接⼝);⽽抽象⼯⼚
模式中,⼀个⼯⼚⽣产多个产品,它们是⼀个产品族,不同的产品族的产品派⽣于不同的抽象产品(或产品接⼝)。
归纳时刻:
⼯⼚模式其实是三种模式:
关键点如下:
⼀、实现越来越复杂
⼆、简单⼯⼚通过构造时传⼊的标识来⽣产产品,不同产品都在同⼀个⼯⼚中⽣产,这种判断会随着产品的增加⽽增加,给扩展和维护带来⿇烦。
三、⼯⼚模式⽆法解决产品族和产品等级结构的问题
四、抽象⼯⼚模式中,⼀个⼯⼚⽣产多个产品,它们是⼀个产品族,不同的产品族的产品派⽣于不同的抽象产品(或产品接⼝)。
可能遇到的问题:
1.何时使⽤⼯⼚模式
根据具体业务需求,不要认为简单⼯⼚是⽤switch case就⼀⽆是处,⽤设计模式是为了解决问题,根据三种模式的特质,以及对未来扩展的预期,来确定使⽤哪种⼯⼚模式。
2.你在项⽬中⼯⼚模式的应⽤
三、观察者模式
⼀个系统中,实现这种⼀对多的⽽且之间有⼀定关联的逻辑的时候,由于需要保持他们之间的协同关系,
最简便的⽅法是采⽤紧耦合,把这些对象绑定到⼀起,这样⼀来,⼀旦有扩展或者修改的时候,开发⼈员所⾯对的难度⾮常⼤,⽽且很容易造成Bug,观察者模式就解决了该问题,在保持⼀系列观察者和观察者对象协同⼯作的同时,之间解耦。
//被观察者
public interface IObject
{
list<IMonitor> listMonitor { get;set;} //定义观察者集合,因为多个观察者观察⼀个对象,这⾥⽤集合
string SubjectState { get; set;} //被观察者的状态
void AddMonitor(IMonitor monitor); //添加⼀个观察者
void RemoveMonitor(IMonitor monitor); //移除⼀个观察者
void SendMessage();  //向所有观察者发消息
}
class Subject :IObject
{
list<IMonitor> listMonitor=new lit<IMonitor>();
string subjectState
{
get; set;//被观察者的状态
}
list<IMonitor> listMonitor
{
get{ return listMonitor;
listMonitor=vale;
}
}
void AddMonitor(IMonitor monitor)
{
list.add(monitor);
}
void removeMonitor(IMonitor monitor)
{
void SendMessage()
{
foreach(IMonitor m in listMonitor)
{
m.update();
}
}
}
//观察者
public :
interface IMonitor //定义观察者接⼝
{ void  update();}
class Monitor:IMonitor
{
string monitorState="Stop!"; //观察者初始状态
string name;      //名称,⽤于标记不同观察者
IObject subject;  //被观察者对象
Monitor(IObject subject,string name) //构造观察者时,传⼊被观察者对象,以及表⽰该观察者名称
{
this.subject=subject;
this.name=name;
Console.WriteLine("我是观察者{0},我的初始状态是{1}",name,monitorState);
}
void Update()  //当被观察者状态改变,观察者要随之改变
{
monitorState=subject.SubjectState;
Console.WriteLine("我是观察者{0},我的状态是{1}",name,monitorState);
}
}
/
/主函数中使⽤
void main()
{
IObject subject=new Subject();
subject.AddMonitor(new Monitor(subject,"Monitor_1"));
subject.AddMonitor(new Monitor(subject,"Monitor_2"));
subject.AddMonitor(new Monitor(subject,"Monitor_3"));
subject.SubjectState="Start!";
subject.SendMessage();
Console.Read();
}
}
讲解:
关键点1:每个观察者需要被保存到被观察者的集合中,并且被观察者提供添加和删除的⽅式
再看互动关系:当添加⼀个观察者的时候,把被观察者对象以构造函数的形式传给了观察者。最后我让被观察者执⾏sendmessage⽅法,这时会触发所有观察者的update⽅法以更新状态。
关键点2:倍观察者把⾃⼰传给观察者,当状态改变后,通过遍历或循环的⽅法逐个通知列表中的观察者
关键点3:虽然解耦了观察者和被观察者的依赖,让各⾃的变化不⼤影响另⼀⽅的变化,但是这种解耦并不是很彻底,没有完全解除两者之间的耦合。
关键点4:在事件中,订阅者和发布者之间是通过把事件处理程序绑定到委托,并不是把⾃⾝传给对⽅。所以解决了观察者模式中不完全解耦的问题。
可能的问题:
1。通过委托绑定⽅法来实现观察者模式,会有什么隐患?
有的,通过+=去把⽅法绑定到委托,很容易忘记-=。如果只绑定不移除,这个⽅法会⼀直被引⽤。我们知道GC去回收的时候,只会处理没有被引⽤的对象,只要是还被引⽤的对象时不会被回收掉的。所以如果在长期不关闭的系统中(⽐如监控系统),⼤量的代码使⽤+=⽽不-=,运⾏时间长以后可能会内存溢出。
2.事件,委托,观察者之间的关系
委托是⼀种类型,事件是⼀种特殊的委托,观察者模式是⼀种设计模式,事件的机制是观察者模式的⼀种实现,其中订阅者和发布者通过委托实现协同⼯作。
归纳:观察者模式的关键点
上述4点已指出。
注意:
使⽤委托绑定⽅法时,需要注意移除⽅法,否则可能造成内存溢出。
委托、事件、观察者模式理解的不够深⼊。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。