软件编程总原则:低耦合,高内聚
一、设计模式中类的关系
1.1.依赖:
Java中表现为:类A使用类B,其中类B是作为类A的方法参数,方法中的局部变量或者静态方法调用。
[code]
public class People{
public void read(Book book){
System.out.println("您读的书是:"+book.getName());
}
}
[/code]
1.2.关联:
1.2.1单向关联:
Java中表现为:类A使用了类B,其中类B是作为类A的成员变量。
[code]
public class Son{
Father father=new Father();
public void getGift(){
System.out.println("从"+father.getName()+"获得礼物");
}
}
public class Father{
public void giveGift(){
System.out.println("送给"+son.getName()+"礼物");
}
}
[/code]
1.2.2双向关联:
Java中表现为:类A中使用类B作为成员变量;同时类B中也使用了类A作为成员变量。
[code]
public class Son{
Father father=new Father();
public void getGift(){
System.out.println("从"+father.getName()+"获得礼物");
}
}
public class Father{
Son son=new Son();
public void giveGift(){
System.out.println("送给"+son.getName()+"礼物");
}
}
[/code]
1.3.聚合:
Java中表现为:聚合为关联的一种,但关联关系的对象是相互独立的,聚合关系的对象是存在着包容关系的。(整体-个体的关系)
[code]
public class People{
Car car;
House house;
public void setCar(Car car){
this.car=car;
}
public void setHouse(House house){
this.house=house;
}
public void driver(){
System.out.println("车的型号为:"+car.getType());
}
public void sleep(){
System.out.println("房子地址为:"+house.getAddress());
}
}
[/code]
1.4.组合:
Java中表现为:组合关系的对象也是存在着包容关系(整体-部分关系),整体和部分是共生共死的。
[code]
public class People{
Soul soul;
Body body;
public People(Soul soul,Body body){
this.soul=soul;
this.body=body;
}
public void study(){
System.out.println("学习所用的灵魂:"+soul.getName());
}
public void eat(){
System.out.println(“吃饭所用的身体”+body.getName());
}
}
[/code]
1.5.继承:
Java中表现为:类与类之间,接口与接口之间的父子关系。
1.6.实现:
Java中表现为: 类实现一个或多个接口的方法。
二、设计模式六大原则
2.1. 单一职责原则:
定义: 不要存在多于一个导致类变更的原因。即:一个类只负责一项职责。
问题: 可能存在职责扩散破坏单一职责原则。
解决:在职责扩散到无法控制的程度前,立刻对代码进行重构。
个人总结:单一职责,代码重构。
2.2.里氏替换原则:
定义:所有引用基类的地方必须能透明地使用其子类的对象。
问题:现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
解决:类B继承类A时,除了添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类的方法。
若非要重写父类的方法才能完成新增的P2功能,则将程序变成:父类和子类都继承一个积累,原有的继承关系取消,采用依赖、聚合、组合等关系代替。
个人总结:子类可以扩展父类的功能,但不能改变父类原有的功能。
(1)子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法
(2)子类中可以增加自己的方法
(3)当子类的方法重载父类的方式时,方法的前置条件(形参)只能比父类更为宽松
(4)当子类的方法实现父类的抽象方法时,方法的后置条件(返回值)要比父类更为严格
2.3.依赖倒置原则:
定义:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
问题:类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
解决:将类A修改为依赖接口I,类B和类C分别实现接口I,类A通过接口I间接与类B或者类C发生联系。
个人总结:抽象的东西比较稳定,逻辑层与操作层之间通过接口来维护关系,操作层中实现接口即可。依赖倒置的核心就是面向接口编程。
PS:
(1)TDD开发模式就是依赖倒置的应用。
TDD:测试驱动开发->开发功能代码前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码(解决开发和测试脱节)
BDD:行为驱动开发->是一种敏捷软件开发技术,从用户需求出发,强调系统行为(解决需求和开发脱节)
ATDD:验收测试驱动开发->强调如何实现系统以及如何检验(解决开发和测试脱节)
(2)传递依赖关系的三种方式:
1>接口传递
2>构造方法传递
3>setter方法传递
2.4.接口隔离原则:
定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
问题:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
解决:将臃肿的接口I拆分成几个接口,分别建立依赖关系。
个人总结:接口尽量小,但要有限度。
2.5. 迪米特原则(最少知道原则):
定义:一个对象应该对其他对象保持最少的了解。
问题:类与类的关系越密切,耦合度越大,当一个类发生改变时,对另外一个类的影响也很大。
解决:尽量降低类与类的耦合。类除了提供public方法,不对外泄漏任何信息。只与直接的朋友通信。
个人总结:迪米特原则使用要适度,要保证结构清晰。
2.6.开闭原则:
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
问题:在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
解决:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
个人总结:用抽象构建框架,用实现扩展细节。
总结:单一职责原则告诉我们实现类要职责单一;里氏替换原则告诉我们不要破坏继承体系;依赖倒置原则告诉我们要面向接口编程;接口隔离原则告诉我们在设计接口的时候要精简单一;迪米特法则告诉我们要降低耦合。而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
设计1、设计2属于良好的设计,他们对六项原则的遵守程度都在合理的范围内;设计3、设计4设计虽然有些不足,但也基本可以接受;设计5则严重不足,对各项原则都没有很好的遵守;而设计6则遵守过度了,设计5和设计6都是迫切需要重构的设计。
三、 23种设计模式
3.1.单例模式(使用反射方式,将会得到新的单例)
定义:确保一个类只有一个实例,而且自信实例化并向整个系统提供这个实例。
类型:创建类模式
类图:
java中饿汉式单例要优于懒汉式单例。C++中则一般使用懒汉式单例。
3.1.1饿汉式单例模式:在单例类被加载时候,就实例化一个对象交给自己的引用。
[code]
public class Singleton{
private static Singleton singleton=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
[/code]
3.1.2懒汉式单例模式:在调用取得实例方法的时候才会实例化对象。(使用synchronized后仍然为现成安全)
[code]
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton==nul){
singleton=new Singleton();
}
return singleton;
}
}
[/code]
PS: jvm垃圾回收机制不会将长期不用的单例类对象当作垃圾(除非人为断开单例中静态引用到单例对象的联接)。验证程序:
[code]
class Singleton {
private byte[] a = new byte[6*1024*1024];
private static Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
class Obj {
private byte[] a = new byte[3*1024*1024];
}
public class Client{
public static void main(String[] args) throws Exception{
Singleton.getInstance();
while(true){
new Obj();
}
}
}
[/code]
运行参数:-verbose:gc -Xms20M -Xmx20M
3.2.工厂方法模式
定义:定义一个用于创建对象的接口时,让子类决定实例化哪个类,工厂方法使一个类的实例化延迟到其子类。
类型:创建类模式
类图:
[code]
interface IProduct{
public void productMethod();
}
class Product implements IProduct{
public void productMethod(){
System.out.println("产品");
}
}
interface IFactory{
public IProduct createProduct();
}
class Factory implements IFactory{
public IProduct createProduct(){
return new Product();
}
}
public class Client{
public static void main(String[] args){
IFactory factory=new Factory();
IProduct product=factory.createProduct();
product.productMethod();
}
}
[/code]
工厂方法模式四个要素:
1>工厂接口:工厂接口是工厂方法模式的核心
2>工厂实现:工厂实现决定如何实例化产品
3>产品接口:产品接口的主要目的是定义产品的规范
4>产品实现:实现产品接口的具体类,决定了产品在客户端中的具体行为
PS:工厂模式根据抽象程度不同可以分为:简单工厂模式,工厂方法模式,抽象工厂模式
3.3.抽象工厂模式
定义:为创建一组相关或相互依赖的对象提供一个接口,而无需指定他们的具体类
类型:创建类模式
类图:
抽象工厂模式针对的是多个产品等级结构,而工厂方法模式针对的是一个产品等级结构。
[code]
interface IProduct1{
public void show();
}
interface IProduct2{
public void show();
}
clas Product1 implements IProduct1{
public void show(){
System.out.println("这是1型号产品");
}
}
class Product2 implements IProduct2{
public void show(){
System.out.println("这是2型号产品");
}
}
interface IFactory{
public IProduct1 createProduct1();
public IProduct2 createProduct2();
}
class Factory implements IFactory{
public IProduct1 createProduct1(){
return new Product1();
}
public IProduct2 createProduct2(){
return new Product2();
}
}
public class Client{
public static void main(String[] args){
IFactory factory=new Factory();
factory.createProduct1().show();
factory.createProduct2().show();
}
}
[/code]
3.4.建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
类型:创建类模式
类图:
建造者模式四个要素:
1>产品类:产品类是一个较为复杂的具体类
2>建造者:实现抽象建造者的所有未实现的方法,一般包括:组建产品,返回组建好的产品
3>抽象建造者:引入抽象构造者的目的是为了将建造的具体过程交给它的子类类实现
4>导演类:负责调用适当的建造者来组建产品。导演类不与任何产品类发生依赖关系,与导演类直接交互的是建造者类。
[code]
class Product{
private String name;
private String type;
public void showProduct(){
System.out.println("名称:"+name);
System.out.println("型号:"+type);
}
public void setName(String name){
this.name=name;
}
public void setType(String type){
this.type=type;
}
}
abstract class Builder{
public abstract void setPart(String name,String type);
public abstract Product getProduct();
}
class ConcreteBuilder extends Builder{
private Product product=new Product();
public Product getProduct(){
return product;
}
public void setPart(String name,String type){
product.setName(name);
product.setType(type);
}
}
class Director{
private Builder builder=new ConcreteBuilder();//派生类赋给抽象类
public Product getAProduct(){
builder.setPart("宝马","X7");
return builder.getProduct();
}
public Product getBProduct(){
builder.setPart("奥迪","Q5");
return builder.getProduct();
}
}
public class Client{
public static void main(String[] args){
Director director=new Director();
Product product1=director.getAProduct();
product1.showProduct();
Product product2=director.getBProduct();
product2.showProduct();
}
}
[/code]
3.5.原型模式(针对对象的复制,适用于大对象的复制,因为Object类的clone方法是一个本地方法,直接操作内存中二进制流)
定义:用原型实例制定创建对象的种类,并通过拷贝这些原型创建信的对象
类型:创建类模式
类图:
原型模式的核心是:原型类Prototype。(需要实现Cloneable接口,重写Object类中的clone方法)
[code]
class Prototype implements Cloneable{//实现Cloneable接口
public Prototype clone(){//重写Object类中clone方法,此处为浅拷贝
Prototype prototype=nulll;
try{
prototype=(Prototype)super.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
/*
class Prototype implements Cloneable{//实现Cloneable接口
private ArrayList list=new ArrayList();//此处需要采用深拷贝
public Prototype clone(){//重写Object类中clone方法,此处为深拷贝
Prototype prototype=null;
try{
prototype=(Prototype)supser.clone();
prototype.list=(ArrayList)this.list.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
*/
class ConcretePrototype extends Prototype{
public void show(){
System.out.println("原型模式实现类");
}
}
public class Client{
public static void main(String[] args){
ConcretePrototype cp=new COncretePrototype();
for(int i=0;i<10;++i){
ConcretePrototype clonecp=(ConcretePrototype)cp.clone();
clonecp.show();
}
}
}
[/code]
创建类模式小结:
单例模式:用于得到内存中的唯一对象
工厂方法模式:用于创建复杂对象
抽象工厂模式:用于创建一组相关或相互依赖的复杂对象
建造者模式:用于创建模块化的更为复杂的对象
原型模式:用于得到一个对象的拷贝
3.6.模板方法模式
定义:定义一个操作中算法的框架,将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可以重定义该算法中的某些特定步骤。
类型:行为类模式
类图:
模板方法模式由一个抽象类和一个(或一组)实现类通过继承结构组成。
3.7.中介者模式(调停者模式)
定义:用一个中介者对象封装一系列对象交互,中介者使各对象不需要显示地相互作用,从而使得耦合松散,而且可以独立改变它们之间的交互
类型:行为类模式
类图:
[code]
abstract class AbstractColleague{
protected int number;
public int getNumber(){
return number;
}
public void setNumber(int number){
this.number=number;
}
public abstract void setNumber(int number,AbstractMediator am);
}
class ColleagueA extends AbstractColleague{
public void setNumber(int number,AbstractMediator am){
this.number=number;
am.AaffectB();
}
}
class ColleagueB extends AbstractColleague{
public void setNumber(int number,AbstractMediator am){
this.number;
am.BaffectA();
}
}
abstract class AbstractMediator{
protected AbstractColleague A;
protected AbstractColleague B;
public Abstract ediator(AbstractColleague a,AbstractColleague b){
A=a;
B=b;
}
public abstract void AaffectB();
public abstract void BaffectA();
}
class Mediator extends AbstractMeidator{
public Mediator(AbstractColleague a,AbstractColleague b){
super(a,b);
}
public void AaffectB(){
int number=A.getNumber();
B.setNumber(number*100);
}
public void BaffectA(){
int number=B.getNumber();
A.setNumber(number/100);
}
}
public class Client{
public static void main(String[] args){
AbstractColleague collA=new ColleagueA();
AbstractColleague collB=new ColleagueB();
AbstractMediator am=new Mediator(collA,collB);
....
}
}
[/code]