面试常问的几大设计模式(一)

本博客内容:
一、单例模式
二、工厂模式
三、观察者模式

装饰者模式
适配器模式
代理模式

设计模式不是高深技术,奇技淫巧,只是一种设计思想,针对不同的业务场景,最本质的目的是解耦,为了可扩展性和健壮性。

一、单例模式

参考:http://blog.jobbole.com/109449/
关键点有4个:
1.私有构造函数
2.声明静态单例对象
3.构造单例之前要加锁
4.需要2次检查单例实例是否为空,分别在锁之前和锁之后

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型吗?
不行,锁住的必须是个引用类型,如果锁值类型,每个不同的线程在声明的时候值类型变量的地址都不一样,那么上个线程锁住的东西,下个线程进来会认为根本没有锁,相当于每次都锁了不同的门。而引用类型的变量地址是相同的,每个线程进来判断锁都是判断同一个地址,相当于锁在同一扇门,起到了锁的作用。

二、工厂模式

参考:http://blog.jobbole.com/109594/
核心功能:根据“需求”生产“产品”
核心思想:解耦“需求”“工厂”和“产品”。
实际上根据业务情景不同分为不同的实现方式。一般分3种:简单工厂、工厂、抽象工厂
1.简单工厂

int prodNo;
class IProduct
{
    public:
    SimpleFactroy(int proNo)
    {
        this.prodNo=prodNo;
    }
    IProduct GetProduct()
    {
        switch(prodNo)
            {
                case 1:
                    return new ProductA();
                case 2:
                    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.你在项目中工厂模式的应用

三、观察者模式

参考:http://blog.jobbole.com/109845/
设计模式三杰:单例、工厂、观察者
因为这三个使用频率最高、变化最多、覆盖面最广
有2个角色:观察者和被观察者
每个角色都对应一个类,比如观察者模式,肯定对应着一个观察者类,被观察者肯定对应的被观察者类。
那么设计模式实际上就是面向对象的特性。
逻辑上:一对多的依赖关系。
就像:守卫们盯着囚犯,一旦囚犯动,守卫们就必须马上采取行动。(守卫:观察者)。

一个系统中,实现这种一对多的而且之间有一定关联的逻辑的时候,由于需要保持他们之间的协同关系,
最简便的方法是采用紧耦合,把这些对象绑定到一起,这样一来,一旦有扩展或者修改的时候,开发人员所面对的难度非常大,而且很容易造成Bug,观察者模式就解决了该问题,在保持一系列观察者和观察者对象协同工作的同时,之间解耦。

//被观察者
public interface IObject
{
    list listMonitor { get;set;} //定义观察者集合,因为多个观察者观察一个对象,这里用集合
    string SubjectState { get; set;} //被观察者的状态
    void AddMonitor(IMonitor monitor); //添加一个观察者
    void RemoveMonitor(IMonitor monitor); //移除一个观察者
    void SendMessage();  //向所有观察者发消息
}
class Subject :IObject
{
    list listMonitor=new lit();
    string subjectState
    {
    get; set;//被观察者的状态
    } 
    list listMonitor
    {
        get{ return listMonitor;
                listMonitor=vale;
            }
    }
    void AddMonitor(IMonitor monitor)
    {
        list.add(monitor);
    }
    void removeMonitor(IMonitor monitor)
    {
        list.remove(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点已指出。
注意:
使用委托绑定方法时,需要注意移除方法,否则可能造成内存溢出。

委托、事件、观察者模式理解的不够深入。

你可能感兴趣的:(设计模式)