工厂模式对修改封闭,对扩展开放的迷惑

kang1happy 2014-03-27 03:34:19
最经在学习工厂模式时遇到一个迷惑的问题:
场景如下:
有一个IDAL层创建一个IUserDAL接口,该接口有UpdateStatus一个方法
DAL层创建一个UserDAL类实现IUserDAL接口,并实现IUserDAL的UpdateStatus方法
DAL层创建一个InnerUserDAL类实现IUserDAL接口,并实现IUserDAL的UpdateStatus方法
DAL层创建一个OuterUserDAL类实现IUserDAL接口,并实现IUserDAL的UpdateStatus方法
Factory类分别有三个方法返回UserDAL,InnerUserDAL,InnerUserDAL的实例
结构就是这样,现在用户提出一个需求,需要更新UserName,我就需要在IUserDAL里面新增一个方法UpdateUserName
同时,修改IDAL新增UpdateUserName,由于修改了接口,就需要修改所有实现接口的类,需要所有类都要实现接口的所有方法,所以我必须修改InnerUserDAL,OuterUserDAL同时新增UpdateUserName方法。

我觉得这样就改起来很麻烦,并且违背了开放封闭原则,请高手来帮忙解惑。
所以按照我的理解,我只需要在UserDAL里面扩展一个UpdateUserName的方法,却由于工厂模式导致需要去修改IDAL的IUserDAL,是我选择的设计模式不合理,还是我的理解有问题?
...全文
342 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
缪军 2014-03-30
  • 打赏
  • 举报
回复
你把业务逻辑的东西放在了DAL, 如果每次增加或者更新一些查询方法,还需要修改DAL,那干脆不要分层了, 分层是为了重复利用已有的设计, 分层设计的目的是使得绝大部分的设计可以在不同的项目中重复利用的, 按照你的设计,你看看换个不同的项目,你还有多少可以重复使用
缪军 2014-03-30
  • 打赏
  • 举报
回复
引用 17 楼 kang1happy 的回复:
但是同你说的DAL层负责数据访问,如果已有的方法是更新Status栏位,我现在需要更新UserName栏位,所以我还是需要在DAL层新增方法。
你看看微软提供的DAL:ado.net的组织架构就知道了,它是通用的, 无论你有多少查询方法,跟DAL没有关系
kang1happy 2014-03-28
  • 打赏
  • 举报
回复
引用 15 楼 sp1234 的回复:
那个所谓的“原则”,就是说一旦确定作为一个公开版本,那么你就不要再去修改它了。 而你则相反,你去修改它,却要来遵守这个原则。当然不可能能。 最后,原则都是有其条件和环境的。任何实用的原则将来都会有被推翻的那一天,主要是先想好应用范围和坚持的时间。
是我一开始选择这个原则就不对,不应该硬套原则,需要有自己的理解和变通,我觉得【ycg_893】给的方案是可行的。
kang1happy 2014-03-28
  • 打赏
  • 举报
回复
引用 13 楼 microtry 的回复:
DAL只负责数据访问,那么业务逻辑的变化,绝对不会涉及到DAL的变化, DAL层中根本就不应该出现User或者UserName之类的对象
你说的这个很好,虽然跟我的问题不相关,但是提出了我犯的一个错误【DAL层应与model无关】,但是同你说的DAL层负责数据访问,如果已有的方法是更新Status栏位,我现在需要更新UserName栏位,所以我还是需要在DAL层新增方法。
kang1happy 2014-03-28
  • 打赏
  • 举报
回复
引用 11 楼 ycg_893 的回复:

public interface IUserDAL
    {
        IList Query();

        void Login(string UserCode);
        
        //增加新函数
    }

    public abstract class AUserDAL : IUserDAL
    {

        public abstract IList Query();

        public virtual void Login(string UserCode)
        {
            if (UserCode != "admin")
            {
                throw new Exception("用户不存在。");
            }
        }

        //实现新函数若有可能不同则添加 virtual 关键字
    }

    public class InnerUserDAL : AUserDAL
    {
        public override IList Query()
        {
            return null;
        }
    }

    public class UserDAL : AUserDAL
    {
        public override IList Query()
        {
            return null;
        }

        public override void Login(string UserCode)
        {
            if (UserCode != "system")
            {
                throw new Exception("用户不存在。");
            }
        }
    }

你说的这个我赞同,也理解作为接口一般创建了是不会去修改的,所以我感觉我这个地方还继续套用工厂模式就会遇到问题,理解以后我感觉模式都是死的,还是需要具体情况灵活应变的好。
  • 打赏
  • 举报
回复
那个所谓的“原则”,就是说一旦确定作为一个公开版本,那么你就不要再去修改它了。 而你则相反,你去修改它,却要来遵守这个原则。当然不可能能。 最后,原则都是有其条件和环境的。任何实用的原则将来都会有被推翻的那一天,主要是先想好应用范围和坚持的时间。
  • 打赏
  • 举报
回复
引用 4 楼 kang1happy 的回复:
但是我觉得这没解决根本问题,对我来说我的重点的,我只是想扩展UserDAL,给它新增一个UpdateUserName的方法,却要动到它的接口IUserDAL,假如我InnerUserDAL,OuterUserDAL都没有实现IUserDAL接口,也就是说他们都有自己单独实现的接口,与IUserDAL没关系。所以我问题的最重心是我只是想扩展UserDAL,给它新增一个UpdateUserName的方法,不想去修改其他的地方,我的模式是不是需要做其他的调整,是不是用了工厂模式,这就是它的弊端了,无法避免?
如果你去修改你发布的接口,你如何保持“对修改封闭”呢? 如果要把一种设计定型下来,并且要扩展其子类型,应该这样写
public interface IDAL
{
   ......
}

public interface IDAL_V2: IDAL
{
.....
}

public interface IDAL_V3:IDAL, iDAL_V2
{
.......
}
缪军 2014-03-27
  • 打赏
  • 举报
回复
DAL只负责数据访问,那么业务逻辑的变化,绝对不会涉及到DAL的变化, DAL层中根本就不应该出现User或者UserName之类的对象
  • 打赏
  • 举报
回复
首先你要知道,接口一旦定义好,没有足够理由就不应该再去修改 像你这种情况可以重新定义一个接口,至于继不继承老接口,还是业务来决定 .Net中也有类似的接口和类
ycg_893 2014-03-27
  • 打赏
  • 举报
回复

public interface IUserDAL
    {
        IList Query();

        void Login(string UserCode);
        
        //增加新函数
    }

    public abstract class AUserDAL : IUserDAL
    {

        public abstract IList Query();

        public virtual void Login(string UserCode)
        {
            if (UserCode != "admin")
            {
                throw new Exception("用户不存在。");
            }
        }

        //实现新函数若有可能不同则添加 virtual 关键字
    }

    public class InnerUserDAL : AUserDAL
    {
        public override IList Query()
        {
            return null;
        }
    }

    public class UserDAL : AUserDAL
    {
        public override IList Query()
        {
            return null;
        }

        public override void Login(string UserCode)
        {
            if (UserCode != "system")
            {
                throw new Exception("用户不存在。");
            }
        }
    }

ycg_893 2014-03-27
  • 打赏
  • 举报
回复
定义一个抽象类叫AUserDAL,并实现IUserDAL接口, UserDAL\InnerUserDAL\OuterUserDAL 这个都继承 AUserDAL,当你新增接口函数时,若都是通用或可调用实现类已有参数数就能实现的,则只需修改 AUserDAL 类,若个别有差异的,则在差异类实现类重写。 同一接口有多个实现类,并且有部份功能相同的话,建议使用抽象类,这样很方便扩展,包括接口扩展。
kang1happy 2014-03-27
  • 打赏
  • 举报
回复
工厂模式的好处就是对创建对象的过程做了封装,并且对对象中具体方法的操作也做了封装,创建者不知道对象是怎么创建的,以及方法的具体执行做了隐藏,怎么样才能达到我的目的,又能实现用户的需求,并且最少的修改代码,并且不影响已有功能。
kang1happy 2014-03-27
  • 打赏
  • 举报
回复
但是用户提的这个需求是一个很平常的需求,不存在不合理,也不应该避免,程序是为用户服务的,设计模式是为软件开发服务的,应该是更能方面开发和设计,所以我想是否我这里的选择不对。有人有更好的方案去解决这种问题吗。
cheng2005 2014-03-27
  • 打赏
  • 举报
回复
设计模式不是圣经,只是解决特定问题的一种较优的选择。 具体到你这个问题来说,你改动的是双方的约定,这种改动本身就属于影响比较大的一个改动了。因为约定一变,约定的实现者和调用者肯定是要跟着变化的。 再回到你标题写的“对修改封闭,对扩展开放”,这不是说你的接口里多一个方法,而是说多一个实现接口的具体产品的情况。所谓的扩展,扩展的是实现接口的具体产品,而不是接口本身进行扩展。接口本身的变化已经破环了设计本身,是应该尽量避免的的行为,牵扯面肯定很广。
rtdb 2014-03-27
  • 打赏
  • 举报
回复
引用 4 楼 kang1happy 的回复:
但是我觉得这没解决根本问题,对我来说我的重点的,我只是想扩展UserDAL,给它新增一个UpdateUserName的方法,却要动到它的接口IUserDAL,假如我InnerUserDAL,OuterUserDAL都没有实现IUserDAL接口,也就是说他们都有自己单独实现的接口,与IUserDAL没关系。所以我问题的最重心是我只是想扩展UserDAL,给它新增一个UpdateUserName的方法,不想去修改其他的地方,我的模式是不是需要做其他的调整,是不是用了工厂模式,这就是它的弊端了,无法避免?
对工厂模式的调用者来说,不存在UserDAL/InnerUserDAL/OuterUserDAL, 它只知道接口IUserDAL。 如果你只扩展UserDAL, 当然没问题,只是外面看不到而已。 所以问题的关键就是新增的UpdateUserName方法是不是要被外面看到。
kang1happy 2014-03-27
  • 打赏
  • 举报
回复
并且按照你说的,我不去修改IUserDAL接口,而是去新增新的接口,让UserDAL去实现它,但是这样就有问题了,我工厂模式返回的实例是IUserDAL,对这个接口产生的实例,就不会有新增接口的UpdateUserName方法。。。 IUserDAL user=(IUserDAL )Factory.CreateUserDAL();
kang1happy 2014-03-27
  • 打赏
  • 举报
回复
但是我觉得这没解决根本问题,对我来说我的重点的,我只是想扩展UserDAL,给它新增一个UpdateUserName的方法,却要动到它的接口IUserDAL,假如我InnerUserDAL,OuterUserDAL都没有实现IUserDAL接口,也就是说他们都有自己单独实现的接口,与IUserDAL没关系。所以我问题的最重心是我只是想扩展UserDAL,给它新增一个UpdateUserName的方法,不想去修改其他的地方,我的模式是不是需要做其他的调整,是不是用了工厂模式,这就是它的弊端了,无法避免?
qixin000 2014-03-27
  • 打赏
  • 举报
回复
再啰嗦一句:接口是整个程序的合同契约,如果你变动了接口,也就是擅自修改了合同,既然违约了,就要承担违约带来的后果!呵呵
qixin000 2014-03-27
  • 打赏
  • 举报
回复
写错一个地方 class Factory { A getAA(); B getBB();} 修改成 class Factory { A getAA(); A getBB();}
qixin000 2014-03-27
  • 打赏
  • 举报
回复
你动了你设计的程序结构的根基(接口),所以导致了全部都得修改! 你可以试着多重实现接口,而不是去修改一个接口,可以达到只修改你实现的接口类! interface A { function a } class AA implements A class BB implements A class Factory { A getAA(); B getBB();} 当你需要添加一个函数时,可新建一个接口B interface B {function b} class AA implements A,B 你这样实际并不需要修改BB以及Factory类,也能达到目的

110,537

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧