依赖转置矩阵原则生活实例

保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
单击提交则表示您同意developerWorks
的条款和条件。 .
所有提交的信息确保安全。
developerWorks 社区:
我的概要信息
选择语言:
可复用面向对象软件的基础 -- 设计模式,以其可复用的设计初衷、精巧的逻辑思维被广大面向对象程序设计所追捧。但不少程序设计者却经常将思考的问题转换为遇到了什么场景就要用什么模式。这种八股文式的思维在某种程度上严重影响了程序设计的艺术性,并固化了程序设计者的思想,违背了设计模式的初衷。在本文中,作者总结了设计模式背后的核心思想,并提出了几个关键的设计原则,例如面向接口、封装变化、依赖倒置原则、只和朋友交谈等。程序设计者只需在程序设计时遵循这些原则,便会发现原来已经在使用某些设计模式了。
, 软件开发工程师, IBM 中国软件开发中心
刘旭进,IBM 中国开发中心软件工程师,对开源软件、REST、Web Service、Open Search 有浓厚兴趣和深入研究。目前在 Lotus Connections Fils Team 从事 REST Service 开发相关的工作。
引题GOF 的设计模式推出以后,受到程序员的热烈追捧,很多程序员不亦乐乎的埋头苦读甚至背诵其 23 个设计模式,并以熟悉设计模式而自豪。然而,在实际的程序设计中,很多程序员并未能把设计模式应用到自己的场景中。原因有很多,设计模式太多以至于常常被混淆;设计模式应用场景太局限或者程序员自己意识不到应用的场景。综合各种原因,根本原因只有一个,程序员并不能透彻理解,熟练应用设计模式的核心思想。笔者认为,设计模式并不是条条框框,设计模式也不是简单的 23 种。设计模式体现的一种思想是:尽可能的复用,而实现可复用的手段无外乎笔者总结的几个设计原则而已。彻底的忘掉 GOF 的设计模式吧,程序设计应该是一门艺术,而不是备受束缚的那些模式。原则一:封装变化该原则的核心思想是,在程序设计中找出应用中可能需要变化之处,把它们独立出来以便以后可以轻易的改动或者扩充,而不影响不需要变化的部分。事实上如果您回过头去重新阅读设计模式的书籍,您会发现,封装变化几乎是每个设计模式背后的精神所在。所有的模式都提供了一套方法让系统中的某部分改变不会影响其它部分。我们举一个简单的例子,我们建立一个 Car 的基类,有两个继承类 Benz 和 BMW, 具体参见下图 1:图 1. Car 的第一个实现相信大部分人都会这么设计,但是这个设计有什么问题呢?我们看待问题需要以发展的眼光,假如科技发展了,所有的 Car 都可以飞了,怎么办?有人说,很简单,给 Car 加一个 protected 的 fly() 方法,这样 Benz 和 BMW 就都可以飞了,继承真伟大!好,那么如果我需要建立另外一个 Car 的子类玩具车(Toycar), 我们知道玩具车可以 run, 但不能 fly 的。怎么办?还是好办,我们可以重载玩具车的 fly 方法,让他们什么都不干。那好,又一个子类来了,模型车(ModelCar)。模型车不能 run,不能 fly,好办,继续重载他们的这些方法。见下图 2:图 2. Car 的第二个实现如果我们有更多的 Car 的子类呢?有没有觉得有点繁琐,是的,我们需要重载太多的方法了。继承并不能帮我们解决问题!可不可以使用接口,我们可以把 fly 从超类中取出来,分别作为接口,Flyable,这样一来只有 Benz 和 BMW 才实现 Flyable 接口,ToyCar 和 modelCar 并不实现该接口。Run 也作类似处理。见下图 3:图 3. Car 的第三个实现大家可以看到,这其实是一个很笨的办法,除去 description() 方法,我们使用继承需要重载 3 个方法,可是我们使用接口实现则需要额外定义两个接口类和5个方法。接口方法里面并不能有实现代码,而 ToyCar 的 fly 行为和 Benz 的飞行行为也可能不尽相同,那么这就意味着我们需要实现越来越多的 fly() 方法。接口也不行!怎么办?想一想我们的前面提到的设计原则,把变化的和不变化的分离开来,以便我们以后可以轻易的改动和扩充,而不影响其它不需要变化的部分。我们变化的部分是什么?是否可以飞行,是否可以 run,以何种方式飞行?何种方式 run ? Benz,BMW 和 ToyCar 的飞行行为和 run 行为各不相同。我们可以把这些不同的 fly 和 run 抽象出来。见如下图 4:图 4. Car 的第四个实现看到这,也许您应该大概明白接下来应该怎么办了。是的,很简单,我们可以给 Car 类加入飞行行为和 run 行为的实例变量。而在初始化 Car 的子类时传入的具体行为进行初始化,这样每个子类就自然拥有了相应的行为。代码参见如下:清单 1. Car 的实现类public abstract class Car
protected RunBehavior runB
protected FlyBehaviro flyB
public abstract description ();
protected performFly()
flyBehavior.fly();
protected performRun()
runBehavior.run();
public class Benz extends Car
public String description()
System.out.println(“I am Benz!”);
public Benz()
this.runBehavior = new HighSpeedRunBehavior();
this.flyBehavior = new HighFlyingBehavior();
}上述代码中我们实现了 Benz,如果我们要实现 ToyCar,一个不能飞,但可以跑。尝试一下,看看多简单。清单 2. ToyCar 的实现类public class ToyCar extends Car
public String description()
System.out.println(“I am Toy car!”);
public Benz()
this.runBehavior = new LowSpeedRunBehavior();
this.flyBehavior = new NoFlyingBehavior();
}总结回过头来,看看我们前面所作的工作,第一个我要告诉您的是,恭喜您学会了策略模式,上面我们的设计的核心实现就是使用了策略模式。继承和接口不能解决一切问题,尽量的利用组合将为您的设计带来低耦合。尽可能的针对接口或者抽象类而不是实现去编程,试想,如果我们定义的 Car 类组合具体的行为类,也就是实现,那么它就被绑死了,我们不可能以后再改变它的行为。但这样,我们可以在运行时动态的改变 Car 子类的具体行为。这也是我们成功的关键。最重要的,把变化的和不变化的分离出来,封装变化的部分以应对随时改变。原则二:只和朋友交谈继续我们的话题,假设我们有一家汽车公司,可以生产 Benz、BMW、ToyCar 和 ModelCar(姑且这么认为吧,虽然这不太符合常理),那么该如何设计我们的实现呢?很简单,参见下面代码:清单 3. CarCompany 类public class CarCompany
public CarCompany ()
public Car produce(String type)
if(“Benz”.equals(type))
car = new Benz();
else if(“BMW”.equals(type))
car = new BMW();
else if(“ToyCar”.equals(type))
car = new ToyCar();
else if(“ModelCar”.equals(type))
car = new ModelCar();
car.assembly();
car.sprayPainting(); // 喷漆
car.proof();
}老问题,上面的代码有问题么?但从业务逻辑上讲,当然没问题。可是还是要用变化的眼光看问题,上面的代码维护起来成本很高。上面的代码要求我们无论是 Benz、BMW、ToyCar 还是 ModelCar 都不能在将来发生变化。否则,我们这段代码就有维护的成本和风险。有没有更有效的办法,想想我们第一个设计原则:把变化的部分提出去,变化的部分是什么,显然生成 car 的那一段。我们把它提出去,参见下面代码:清单 4. CarFactory 和 CarCompany 另一种实现public class CarFactory
public CarFactory ()
public Car createCar(String type)
if(“Benz”.equals(type))
car = new Benz();
else if(“BMW”.equals(type))
car = new BMW();
else if(“ToyCar”.equals(type))
car = new ToyCar();
else if(“ModelCar”.equals(type))
car = new ModelCar();
public class CarCompany
CarFactory carF
public CarCompany (CarFactory carFactory)
this.carFactory = carF
public Car produce(String type)
Car = carFactory.createCar(type);
car.assembly();
car.sprayPainting();
car.proof();
}很显然,我们的 CarCompany 现在只依赖 CarFactory 一个类了。有人说这么做有什么用,我们只不过把问题转移到另外一个对象 CarFactory 了,问题依然存在。但是别忘了,我们的 CarCompany 可能不止一个 produce() 方法。它可能还有 sale(), repair() 方法。这样,我们相当于是把几个问题缩小为一个问题了。总结这个例子虽然很简单,但是却告诉我们一条最重要的设计原则,一个类应该尽可能少的与其它类产生联系,尽可能的保持类之间的耦合度,保持类的最少知识量。恭喜您,您学会了简单工厂模式。外观模式也是对本原则的典型应用。具体请参见设计模式相关书籍。原则三:依赖倒置原则(DIP)在您的设计里面,一定要减少对具体类的依赖,尽量依赖抽象,不要依赖具体类。这就是依赖倒置原则。听起来有点像面向接口,不针对实现编程。的确很类似,但是这里强调的是抽象。具体说来就是不要让高层组件依赖低层组件,而且不管高层低层组件,都应该依赖抽象。高层组件最多是依赖低层组件的抽象。低层的抽象和实现也只依赖于高层的抽象。所谓高层组件是由其它低层组件定义其行为的类。高层组件是包含重要的业务模型和策略选择,低层模块则是不同业务和策略实现。也许您不是很理解这段话的含义。不要紧,继续我们上面的例子。假设随着汽车公司规模越来越大,业务规模拓展到了亚洲和欧洲。我们希望可以针对亚洲人和欧洲人生产出不同的同一品牌的的汽车。比如同一品牌 BMW 亚洲是左驾驶座(当然除了一些特殊地区),欧洲是右驾驶座。看看下面的实现。清单 5. DependencyCarCompany 实现public class DependencyCarCompany
public CarCompany ()
public Car produce(String style, String type)
if(“Asia”.equals(style))
if(“Benz”.equals(type))
car = new AsiaBenz();
else if(“BMW”.equals(type))
car = new AsiaBMW();
else if(“ToyCar”.equals(type))
car = new AsiaToyCar();
else if(“ModelCar”.equals(type))
car = new AsiaModelCar();
else if(“Europe”.equals(style))
if(“Benz”.equals(type))
car = new EuropeBenz();
else if(“BMW”.equals(type))
car = new EuropeBMW();
else if(“ToyCar”.equals(type))
car = new EuropeToyCar();
else if(“ModelCar”.equals(type))
car = new EuropeModelCar();
car.assembly();
car.sprayPainting(); // 喷漆
car.proof();
}够简单吧!总结它们对象之间依赖的情况如图 5 所示:图 5. CarCompany 的第一个实现我们发现 CarComany 依赖的具体类有 8 个,如果任何一个类发生改变,CarCompany 都需要改变。这至少不符合我们的原则二:只和朋友交谈。应用我原则一,把变化的部分提出来。我们可以定义两个 CarCompany 的子类:AsiaCarCompany 和 EuropeCarCompany 用来生产不同样式的同一品牌的汽车。在这两个子类里面,需要做的就是生成不同品牌和样式的汽车,然后再调用超类的三个方法。这样的话我们可以把生成汽车的方法提出来。清单 6. CarCompany 另一种实现public abstract class CarCompany
public Car produce(String type)
Car car = createCar(type);
car.assembly();
car.sprayPainting(); // 喷漆
car.proof();
protected abstract Car createCar(String type);
public class AsiaCarCompany
protected Car createCar(Sting type)
if(“Benz”.equals(type))
car = new AsiaBenz();
else if(“BMW”.equals(type))
car = new AsiaBMW();
else if(“ToyCar”.equals(type))
car = new AsiaToyCar();
else if(“ModelCar”.equals(type))
car = new AsiaModelCar();
public class EuropeCarCompany
protected Car createCar(Sting type)
if(“Benz”.equals(type))
car = new EuropeBenz();
else if(“BMW”.equals(type))
car = new EuropeBMW();
else if(“ToyCar”.equals(type))
car = new EuropeToyCar();
else if(“ModelCar”.equals(type))
car = new EuropeModelCar();
}DependencyCarCompany 的问题在于,它依赖于每一个具体的 Car 类型。然而,在应用第二种方法后,CarCompany 现在只依赖 Car 类型的抽象,不再依赖具体类型,而是把这些依赖转移到子类中。我们可以画一个对象依赖图 6:图 6. CarCompany 的第二个实现从这个图中我们可以看出:我们的高层组件也就是 CarCompany 已经由原来的 8 个低层对象依赖变化为只依赖一个低层对象的抽象 Car。这就是依赖抽象。对比上面两个图,您会发现,以前所绘制的依赖是自上而下,而现在则是倒置过来。高层和低层组件都依赖于抽象的 Car。这就是依赖的倒置。总结恭喜您,您学会了工厂方法模式。上面的例子实际上是工厂方法的一个典型应用。一些辅助原则可以帮助您更好的运用 DIP:
任何变量都不应该持有一个指向具体类的引用。任何类都不应该从具体类派生,而是派生一个抽象类。任何方法都不应该覆盖它的任何基类中已经实现了的方法。原则四:类应该对扩展开放,对修改关闭继续刚才的例子,随着汽车公司业务越来越大,为了满足不同客户的不同需求,对于任一品牌的汽车我们将有不同型号的配置。我们的配置包括气囊(Balloon)、天窗(SkyLight)以及自动加热座椅(HeatedSeats)等。每款汽车的价格为汽车自身价值加上配件的价格。设计如下图 7:图 7. 第一个实现Oh, My God! 这是什么?类爆炸?!好可怕的一件事。可以想象出来,这样的设计将来的维护成本又多高。假如我想增加新的配件怎么办,假如我想增加新的品牌又怎么办?其实我们可以用实例变量和继承来重构上面的设计。见下图 8: 图 8. 第二个实现我们在超类 Car 里面 cost() 方法计算各种配件的价格,然后在子类里面覆盖 cost() 方法,但是会调用超类 cost 方法得到配件价格和,然后再加上子类汽车的基本价格。清单 7. Car 的另一种实现public abstract class Car
protected B
protected SkyLight skyL
protected HeatedSeat heatedS
protected int cost()
int res = 0;
if(hasBalloon)
res += 25000;
if(hasSkyLight)
res += 20000;
if(hasHeatedSeat)
res +=10000;
void setBalloon(Balloon balloon);
boolean hasBalloon();
public Benz extends Car
public Benz(Balloon blloon)
this.setBalloon(balloon);
public int cost()
int res= 1000000;
res += super.cost();
}怎么样?看起来好像天衣无缝的解决了我们的问题。然而有下面几个问题需要考虑:如果配件的价格发生改变怎么办?如果出现新的配件怎么办?如果某些配件在某种品牌汽车上不能应用怎么办?比如您在玩具车上装 ABS(自动刹车系统)显然是没有意义的。如果我想给我的车安装 4 个气囊而不是一个两个,怎么办?为什么看起来完美的设计,会有这么多解决不了的问题? 因为它违背我们的设计原则:类应该对扩展开放,对修改关闭。我们的目标是允许类容易扩展。在不修改现有代码的基础上,就可以搭配新的行为。这样设计才可以接受新的功能来应对改变的需求。该原则最典型的应用就是装饰模式。让我们以装饰模式的思想重构我们上面的实现。首先用户需要一辆汽车。那我们就构造一辆裸车,并计算价格。用户希望是 BMW, 那我们就把它封装为 BMW,并计算价格。用户希望带有气囊,那我们就给我们的 BMW 装饰上气囊,并加上气囊的价格 25000。用户希望有天窗,那我们就给我们的 BMW 装饰上天窗,并加上气囊的价格 20000。用户希望有加热椅,那我们就给我们的 BMW 装饰上加热椅,并加上的价格 10000。用户希望带有双重气囊,那我们就给我们的 BMW 再装饰上气囊,并加上气囊的价格 25000。见图 9:图 9. 装饰过程参见我们的实现代码。清单 8. Car 的另一种实现public abstract class Car
protected abstract int cost();
public class Benz extends Car
public int cost()
return 100000;
public abstract class CarDecorator extends Car
protected abstract int cost();
public class Balloon extends CarDecorator
public Balloon(Car car)
this.car =
public int cost()
return car.cost() + 25000;
public class SkyLight extends CarDecorator
public SkyLight (Car car)
this.car =
public int cost()
return car.cost() + 20000;
public class HeatedSeat extends CarDecorator
public HeatedSeat (Car car)
this.car =
public int cost()
return car.cost(0 + 10000;
}下面看看我们的测试类。清单 9. Car 装饰者的测试类public class CarWithDecorator {
public static void mian(String[] args)
Car car = new BMW();
car = new Balloon(car);
car = new SkyLight(car);
car = new HeatedSeat(car);
car = new Balloon(car);
System.out.println(car.cost());
}怎么样?回过头,想一想我们前面提出的那四个问题,是用这种设计方式是不是可以很好地解决呢?总结恭喜您,您学会了装饰模式。现实世界中,装饰模式,也即我们面向扩展开放,面向修改关闭的应用很多。最常见的就是 Java I/O。见下图 10:
图 10. Java I/O应用开放封闭原则,有时候会带来小类过多的情况,这是这个原则所带来的潜在问题。所以在实际应用中也要注意设计上的考虑。而不要一味的遵循。结篇设计原则不是统一的,不同人对有不同的设计原则有不同的见解,设计原则也不限于上面所陈述的几点。然后设计原则大的方向是统一的,那就是让代码尽可能的应对变化,尽可能的可复用。设计模式不是万能的,没有设计模式也不是不能的。然而在程序设计过程中遵循一些最基本的设计原则则是一个优秀的程序员所必需的,良好的设计原则的应用可以让您设计的程序从容应对可能的改变,可以让您的代码变得优雅而富有艺术性。
参考 ,了解设计模式基本内容。
查看教程“”,了解设计模式基本词汇以及简单使用设计模式。
查看教程“”,了解设计模式深层次的应用。
查看系列文章“”,了解 JDK 在设计模式中的应用。
:浏览关于这些和其他技术主题的图书。:数百篇关于 Java 编程各个方面的文章。加入 。
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
IBM PureSystems(TM) 系列解决方案是一个专家集成系统
通过学习路线图系统掌握软件开发技能
软件下载、试用版及云计算
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Java technologyArticleID=555513ArticleTitle=超越设计模式publish-date=依赖倒置原则图片_百度百科
小贴示:点击键盘上下方向键也可进行翻页& &依赖倒置,这个概念看起来很玄乎,其实很简单。这也是我看所有技术书的心态,在心态上战胜这本书,那么它的内容,也就能很容易理解了。依赖倒置的英文定义如下:
& &High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions。
& &英文不给力的自动跳过吧。这个原则的意思就是,代码要依赖于抽象,而不依赖于实现。这又是什么意思呢?我有个比喻:公司需要你吗?需要,这叫依赖。但是你觉得你所在的公司离开你就不行了吗?显然不是的,公司只是依赖于具有拥有技能的人。所以,这里的拥有技能的人是你和你同事的抽象,当然也包括社会上的人。那所谓的倒置是什么呢?公司如果依赖你,那就叫正置;但是公司现在依赖的是具有技能的人,是抽象集合体,这就叫倒置。所谓正置是指依赖于具体的实现,而倒置则是依赖于对具体的抽象。
& &1. 依赖倒置有什么好处呢?
& & & &依赖倒置原则的要求是依赖于抽象,那么这个问题就相当于在问,基于抽象编程有什么好处呢?这样问题就容易回答了。抽象是什么?抽象是将一群具有相似特征和行为的个体归为一类。这样,我就不用为每一个个体实现的类定制函数了。试想,加入我有一个函数名叫&杀鸡&,它的参数不是抽象的鸡,而是土鸡,那么当我要杀饲料鸡的时候,还需要将&杀鸡&重载一个参数为饲料鸡的函数。这样会十分麻烦。所以,依赖倒置的好处之一是:
不必为某一具体个体定制特别的处理函数,可以避免大量的无趣的函数重载。
& & & &那还有其他好处吗?当然有了,依赖倒置可以降低具体类之间的耦合性。这是什么意思呢?看下面一段不好的程序:
1 class QiRuiQQ{
void run(){
cout&&"QiRuiQQ running..."&&
8 class Driver{
void drive(Benz bz){
15 int main(){
Driver *d = new Driver();
QiRuiQQ *q = new QiRuiQQ();
d-&drive(*q);
& & & 这两个类的耦合度如何呢?可以告诉你,太高了。为什么呢?试想,假如Driver要开法拉利怎么办?我现在有一个法拉利的类:
1 class Ferrari{
void run(){
cout&&"Ferrari running..."&&
& & & 那Driver只能眼睁睁的看着这样一辆超级跑车,他却开不走。如果要让Driver能开法拉利,我们就需要为Drive加一个重载函数,而这函数体于QiRuiQQ的drive方法惊人的相似。那么我们怎么办呢?这个时候就需要我们伟大的依赖倒置原则了,看符合依赖倒置的代码吧:
*车辆的抽象
4 class ICar{
virtual void run(){}
*司机的抽象
11 class IDriver{
12 public:
virtual void drive(ICar car){}
*车辆的具象:奇瑞QQ
18 class QiRuiQQ:public ICar{
19 public:
void run(){
cout&&"QiRuiQQ running..."&&
*车辆的具象:法拉利
27 class Ferrari:public ICar{
28 public:
void run(){
cout&&"Ferrari running..."&&
*司机的具象
36 class Driver:public IDriver{
37 public:
void drive(ICar* car){
car-&run();
43 int main(){
Driver *d = new Driver();
QiRuiQQ *q = new QiRuiQQ();
d-&drive(q);
system("pause");
运行结果:
& &QiRuiQQ running...& &请按任意键继续. . .
& &此时无论你给司机什么车,司机都会开了,只要那个车继承了ICar。这样就降低了两个类别的耦合度了。所以,依赖倒置的第二个好处就出来了:
降低类间的耦合度。
& &2. 依赖的实现方法有哪些?
& & & &所谓的依赖实现,这个概念貌似很牛逼,其实很简单的,就是怎么把参数传给另一个类。作者给了三种方法,我们来看看到底是哪三种吧。
& & & & &◇a. 构造函数(构造对象的时候传入另一个对象)
& & & & &◇b. setter方法(Java里有Bean的概念,C++也可以完全模仿啊!其实就是一个设置成员属性的值的函数而已)
& & & & &◇c. 接口声明依赖对象(就是我们上面的代码了)
& &1. 依赖倒置原则可以避免大量的函数重载。
& &2. 依赖倒置原则可以降低类间的耦合度。
& &3. 依赖的实现方法有三种,分别是:构造函数、setter方法、接口声明依赖对象。
& &依赖倒置虽然给我们提供了方便之门,但是从上面的代码也可以看出,它很容易类的规模膨胀,如果为每一个类都声明一个接口,那就会膨胀的可怕。所以,对于我们这些技术人来说,在技术的应用上,要有拿捏,不能没有,也不能全都是。
阅读(...) 评论()

我要回帖

更多关于 excel 批量转置 的文章

 

随机推荐