1,依赖?
---什么叫依赖,这就叫依赖,我不能离开你,我要调用你,没有你我实现不了功能,这就是依赖。
---单项依赖:注意在MVC中Controller是依赖Model的,而Model不依赖Controller。这叫单项依赖 。
2,耦合?()
简单地说,软件工程中对象之间的耦合度就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。
---耦合:是指两个或两个以上的体系或两种运动形式间通过相互作用而彼此影响以至联合起来的现象。
3,耦合和依赖的关系?
耦合就是依赖。
4,重点,如何解耦?
---解耦最有效的方法就是使用接口。其次是抽象类。(下面案例就是用一个接口,一个抽象类实现了解耦。)
---面向对象思想一直在追求的就是解耦,目的就是使软件系统能够以更低的代价接受变化,比如增加新业务时,希望不用修改代码或者少修改。(看下边这个案例,抽象变化,就解除了耦合。)
---代码级别的解耦是最常见的,把OOA/OOD(面向对象分析/面向对象设计)做的更好一些,每个类的职责明确,接口功能尽量细分,多使用经典的设计模式,这样的代码也比较容易看懂。
(1),一个双向耦合案例(公司内很多同事上班炒股票,又怕被老板看到,就贿赂前台,老板来了,前台就打电话通知给他们。):
class Secretary //前台秘书类
{
//同事列表组合,用来添加请求帮忙同事。
private IList<StockObserver> observers=new List<StockObserver>();
private string action;
//增加
=110行 public void Attach(StockObserver observer) //注意,这里是从外部传入对象---即依赖注入,但是还是产生了耦合,因为StockObserver是一个具体类,依赖具体等于把代码写死了,类与类之间产生了紧密联系(耦合),以后假如出现变化,怎么办?变一次,改一次,这严重违背了OCP原则,解决办法:依赖抽象,依赖抽象才能应对变化,才能避免耦合。 可以和依赖倒转原则一起记忆:http://www.cnblogs.com/feichengwulai/articles/3620200.html 尤其需要注意的是,即使类是用依赖注入从外部传入对象,但是传入的不是抽象对象,也会产生耦合。看情况把,外部传入的对象能抽象的尽量抽象出来,不要产生耦合。
看最后一个解耦代码和代码结构图,其实就是将两个类(通知类和观察者类)抽象化,然后互相依赖抽象编程,就成功解耦了。
{
observers.Add(observer); //有几个同事请前台帮忙,就给集合增加几个同事对象。
}
//通知
public void Notify()
{
foreach(StockObserver o in observers) //老板来时,就给所有的登记的同事们发通知---“老板来了”
{
o.Update();
}
}
//前台状态
public string SecretaryAction //字符串属性,前台通过电话,所说的话或所做的事
{
get { return action;}
set { action = value;}
}
}
//看股票同事类
class StockObserver
{
private string name;
private Secretary sub; //前台
public StockObserver(string name,Secretary sub)
{
this.name=name;
this.sub=sub;
}
public void Update() //得到前台的通知,赶快采取行动。
{
Console.WriteLine("{0}{1}关闭股票行情,继续工作!",sub.SecretaryAction,name);
}
}
//客户端程序如下:
static void Main(string[] args)
{
//前台小姐小王
Secretary xiaowang=new Secretary();
//看股票同事
StockObserver tongshi1=new StockObserver("王菲",xiaowang);
StockObserver tongshi2=new StockObserver("谢霆锋",xiaowang);
//前台记下了两位同事
xiaowang.Attach(tongshi1);
xiaowang.Attach(tongshi2);
//发现老板回来
xiaowang.SecretaryAction="老板回来了";
//通知两个同事
xiaowang.Notify();
Console.Read();
}
运行结果如下:
老板回来了,王菲 关闭股票行情,继续工作!
老板回来了,谢霆锋 关闭股票行情,继续工作!
---注意:前台类和看股票类之间相互耦合了。前台类要增加观察者(同事类),观察者(同事类)需要前台的状态。
如果观察者(同事类)当中还有人想看NBA的网上直播,你的前台类代码怎么办?那就要改动前台类了。
(2),解耦实践一
//抽象观察者,即需要通知的同事(看股票同事和看NBA同事)的抽象父类
abstract class Observer
{
protected string name;
protected Secretary sub;
public StockObserver(string name,Secretary sub)
{
this.name=name;
this.sub=sub;
}
public abstract void Update();
}
//增加两个具体观察者
//看股票同事
class StockObserver:Observer
{
public StockObserver(string name,Secretary sub):base(name,sub){}
public override void Update() //得到前台的通知,赶快采取行动。
{
Console.WriteLine("{0}{1}关闭股票行情,继续工作!",sub.SecretaryAction,name);
}
}
//看NBA的同事
class NBAObserver:Observer
{
public NBAObserver(string name,Secretary sub):base(name,sub){}
public override void Update() //得到前台的通知,赶快采取行动。
{
Console.WriteLine("{0}{1}关闭NBA直播,继续工作!",sub.SecretaryAction,name);
}
}
---让两个观察者去继承"抽象观察者",对于"Update(更新)"的方法做重写操作。
---下面是前台秘书类的编写,把所有的具体观察者耦合的地方都改成了抽象观察者
class Secretary //前台秘书类
{
//同事列表组合,用来添加请求帮忙同事。
private IList<StockObserver> observers=new List<StockObserver>();
private string action;
//增加
public void Attach(Observer observer) //针对抽象编程,减少了与具体类的耦合
{
observers.Add(observer); //有几个同事请前台帮忙,就给集合增加几个同事对象。
}
//减少
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach(Observer o in observers) //老板来时,就给所有的登记的同事们发通知---“老板来了”
{
o.Update();
}
}
//前台状态
public string SecretaryAction //字符串属性,前台通过电话,所说的话或所做的事
{
get { return action;}
set { action = value;}
}
}
---注意看上边具体观察者代码中,有没有与具体的类耦合的?有,前台秘书就是一个具体的类,也应该抽象出来。假如前台来不及打电话了,那么老板就成了具体的通知者(自己通知自己回来了)。所以这里,观察者也不应该依赖具体的实现,而是一个抽象的通知者。
(2),解耦实践二(做到了两者---观察者和通知者,都不耦合,通过一个接口,一个抽象类,实现了解耦!!!)
---代码结构图如下:
//通知者接口
interface Subject
{
void Attach(Observer observer);
void Detach(Observer observer);
void Notify();
string SubjectState
{
get;
set;
}
}
//具体的通知类可能是前台,也可能是老板,他们也许有各自的一些方法,但对于通知者类说,他们是一样的,所以他们都要去实现这个接口
class Boss:Subject
{
//同事列表组合,用来添加请求帮忙同事。
private IList<StockObserver> observers=new List<StockObserver>();
private string action;
//增加
public void Attach(Observer observer) //针对抽象编程,减少了与具体类的耦合
{
observers.Add(observer); //有几个同事请前台帮忙,就给集合增加几个同事对象。
}
//减少
public void Detach(Observer observer)
{
observers.Remove(observer);
}
//通知
public void Notify()
{
foreach(Observer o in observers)
{
o.Update();
}
}
//老板状态
public string SubjectState
{
get { return action;}
set { action = value;}
}
}
---前台秘书类与老板类类似,略。
---对于具体的观察者,需要更改的地方就是把与"前台"耦合的地方都改成针对抽象通知者。
//抽象观察者
abstract class Observer
{
protected string name;
protected Subject sub;
public StockObserver(string name,Subject sub) //原来是前台类,现改成抽象通知者接口
{
this.name=name;
this.sub=sub;
}
public abstract void Update();
}
//看股票同事
class StockObserver:Observer
{
public StockObserver(string name,Subject sub):base(name,sub){} //原来是前台,现改成抽象通知者
public override void Update() //得到前台的通知,赶快采取行动。
{
Console.WriteLine("{0}{1}关闭股票行情,继续工作!",sub.SubjectState,name); //原来是前台状态,现改成"抽象通知者状态"
}
}
//客户端代码如下:
//老板胡汉三
Boss huhansan=new Boss();
//看股票同事
StockObserver tongshi1=new StockObserver("王菲",huhansan);
//看NBA的同事
NBAObserver tongshi2=new NBAObserver("谢霆锋",huhansan);
huhansan.Attach(tongshi1);
huhansan.Attach(tongshi2);
huhansan.Detach(tongshi1); //王菲其实是没有被老板通知到,所以减去。由于王菲没有被通知到,所以她被当场抓获,下场很惨
//老板回来
huhansan.SubjectState="我胡汉三回来了!";
//发出通知
huhansan.Notify();