`
eXolution
  • 浏览: 30323 次
社区版块
存档分类
最新评论

SOC的小皮鞭抽呀抽,IOC与工厂模式的区别之我看

 
阅读更多

前言
这是一篇使用教程式语言写的技术笔记,浅谈了我对面向对象思想以及设计模式的一些认识与理解。
有别于网上大神们技术文章,我使用较为通俗与平易近人的例子阐述这些知识。也希望感兴趣的童鞋们能收获一些知识。
示例代码使用java写的。如果疏误,欢迎指正。

1、抽象
众所周知,面向对象的最核心部分就是抽象。
抽象,即抽取其本象。是人类观察事物,并提取其本质特征或共同点的一种智慧。
而面向对象的设计哲学就是依赖抽象,而不是依赖具体。因为大千世界事物是千变万化的,但俗话说的好,万变却不离其宗。事物的本质(抽象)往往是非常稳定的。
着眼于软件工程,需求的千变万化是导致软件危机的罪魁祸首,因此一个好的设计就是要以不变应万变。这就是面向对象的初衷。具体而言,对于事物的本质,我们可抽象出其接口(interface )。现在比较热门的接口驱动编程,便是由此而来。因为依赖抽象,可以使客户代码相对稳定和健壮。可以使具体实现的变动,在一定程度上较少的影响客户代码。
举个例子吧。苹果 橘子 梨 这些有什么共同点?他们是可以吃的eatable(我们只分析行为上的本质,这是由这些事物的目的性所决定的) 
class Apple{
public void eat(){
System.out.println("delicious apple!");
}
}
class Orange{
public void eat(){
System.out.println("delicious orange");
}
}
如果我们依赖具体 那么是什么情况呢
class Child{
public void wantToEat(){
Apple apple=new Apple();
apple.eat();
}
}
这时候不想吃苹果了想吃橘子 怎么办 那我们只好修改代码
class Child{
public void wantToEat(){
Orange orange=new Orange();
orange.eat();
}
}
你发现了什么,我们修改了所有的业务逻辑(客户)代码,这样带来的代价是巨大的。

因此,我们使用抽象将其本质抽取出来
interface Eatable{
public void eat();
}
class Apple implements Eatable{
//@重写
public void eat(){
System.out.println("delicious apple!");
}
}
class Orange implements Eatable{
//@重写
public void eat(){
System.out.println("delicious orange");
}
}
class Child{
public void wantToEat(){
Eatable eatble=new Apple();
eatble.eat();
}
}
这样一来,如果想换换口味,只需要修改实例化eatble的代码(new Apple())即可。因此显而易见,我们付出了较少的代价,来应对需求变更。(在这里看起来虽然很粗浅很不起眼,但在实际的工程里代价差距可能是惊人的)

2、工厂模式
然而,问题也来了。虽然我们依赖了抽象,但是抽象(eatable)与具体实现(apple)之间的关系(=new),也出现在我们的客户端代码里,这依旧是一种强耦合。这时候工厂模式出现了。
他使得产品的具体实现(生产)交给工厂处理,而业务逻辑(客户代码)只需要向工厂索要产品即可,他并不关心,工厂如何生产,并且生产什么。只要符合我们之前约定的产品本质即可。
对于,我们的例子,工厂就像一个冰箱。代码如下
interface Eatable{
public void eat();
}
class Apple implements Eatable{
//@重写
public void eat(){
System.out.println("delicious apple!");
}
}
class Orange implements Eatable{
//@重写
public void eat(){
System.out.println("delicious orange");
}
}
class Fridge{
//静态工厂
static public Eatable getEatable(string type){
if("fruit".equals(type)) return new Apple();
elseif("bread".equals(type)) return new Tiramisu();
}
}
class Child{
public void wantToEat(){
Eatable eatble=Fridge.getEatable("fruit");
eatble.eat();
}
}
写到这你们发现了什么,即使我想换口味,甚至不用修改一行客户代码!!只需要修改工厂代码就行了!!
当然了,这时候很多童鞋都会问了。你不还得修改代码么,你new不写在这儿不还得写在其他地儿么?哼 换汤不换药。
很好,这时需要请出我们的小皮鞭SOC了。
3、SOC
即Separation of concerns。关注点分离。是人类的一种解决问题的思维方法。先将复杂的问题做合理的分解,然后再分别仔细研究问题的不同侧面(关注点)。
SOC可以说是很多设计模式的原动力。【包括现在越来越响的AOP(Aspect Oriented Programming面向切面编程)当然此为后话】更有人说,架构就是关注点分离。
SOC带来的好处是使复杂的问题变得清晰,分而治之又为维护和扩展带了很大的便利。而且有点儿像四人帮的SRP(单一职责原则)但他不仅仅在责任上分离,还在视角上分离。
从最早的C语言 .c .h文件的分离,到后来的三层架构、MVC,乃至现在IOC AOP无不体现了这种分离。
嗯 回到我们的问题。虽然工厂并没有消除掉抽象与具体之间的联系,依然会存在new实例化。但是他将产品的生产和使用进行了分离。
然而这种分离却不是很彻底。因为客户代码中出现了工厂代码。我们依然要主动要求工厂去给我们建立依赖关系,虽然是间接依赖关系。
这时候,也该请出做了半天冷板凳的IOC了
4、IOC 
全称是Inversion of Control,控制反转。或者称之为DI--Dependency Inversion 依赖倒置(其实更形象的说是依赖注入)。我们上面说了。 我依然是是主动去要求建立依赖的(体现在调用工厂创建实例)。
而IOC的原理是好莱坞法则:Don't call us, we'll call you(别来找我,我会去找你的!)
也就是说一个对象所依赖的对象,是有其他人主动交给你的。这个其他人是谁?没错就是容器
spring 就是这样的一个容器(容器即提供一系列服务的管理器,而spring就是提供依赖注入等服务的容器)。
回到咱们的例子就是。以前child是从冰箱里自己拿水果吃。而这个child是个娇生惯养的孩子,不想自己拿。
因此每次都是妈妈亲自交给他,他只专注吃这也动作。这也是最彻底的分离。
我们需要稍微修改下代码
将可吃的东西eatable作为child的成员变量(体现依赖)。
使用工厂模式
interface Eatable{
public void eat();
}
class Apple implements Eatable{
//@重写
public void eat(){
System.out.println("delicious apple!");
}
}
class Orange implements Eatable{
//@重写
public void eat(){
System.out.println("delicious orange");
}
}
class Fridge{
//静态工厂
static public Eatable getEatable(string type){
if("fruit".equals(type)) return new Apple();
elseif("bread".equals(type)) return new Tiramisu();
}
}
class Child{
private Eatble eatble;
public void wantToEat(){
Eatable eatble=Fridge.getEatable("fruit");
eatble.eat();
}
}
使用spring的IOC方法。
interface Eatable{
public void eat();
}
class Apple implements Eatable{
//@重写
public void eat(){
System.out.println("delicious apple!");
}
}
class Orange implements Eatable{
//@重写
public void eat(){
System.out.println("delicious orange");
}
}
class Fridge{
//静态工厂
static public Eatable getEatable(string type){
if("fruit".equals(type)) return new Apple();
elseif("bread".equals(type)) return new Tiramisu();
}
}
class Child{
private Eateble eatble;
//构造注入
public Child(Eatable eatble){
this.eatble=eatble;
}
public void wantToEat(){
this.eatble.eat();
}
}
看到这,你应该已经发现了工厂模式与IOC的区别。客户代码中再也没了讨厌的new 和可恶的冰箱了。我只需要专注一件事,就是吃!(吃不死你)
而这个eatble是完全是由spring容器从Child构造函数中注入进来的。整个业务逻辑清晰明了,这就是传说中高内聚,低耦合啊!
当然,我们还是要付出一定的代价。要写一些注入代码(这里牵涉spring的语法细节 不再赘述)。
虽然如此,但这确实比较完美的做到关注点分离。同时也为实现AOP提供了技术基础。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics