设计模式第一阶段:
六大原则
1、开闭原则:对扩展开放,对修改关闭。
2、里氏代换原则:任何基类可以出现的地方,子类一定可以出现。
3、依赖倒转原则:依赖于抽象而不依赖于具体。
4、接口隔离原则:使用多个隔离的接口,比使用单个接口要好。
5、迪米特法则,又称最少知道原则:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
6、合成复用原则:尽量使用合成/聚合的方式,而不是使用继承。
一、工厂模式
作用:一个抽象的接口,多个抽象接口的实现类,一个工厂,用来实例化抽象的接口。
优点:一个调用者想创建一个对象,直接向工厂请求即可,不需要知道具体实现,以提高系统的可维护性、可扩展性。
缺点:产品修改时,工厂也要进行修改。
二、抽象工厂模式
作用:抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。
优点:当一个产品类型(每一类中有多个产品)被设计成一起工作时,有良好的维护性。
缺点:产品类扩展困难。
三、单例模式
作用:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
四、适配器模式
作用:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
模式中的角色
1.目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
2.需要适配的类(Adaptee):需要适配的类或适配者类。
3.适配器(Adapter):实现了目标接口,通过包装一个需要适配的对象,把原接口转换成目标接口。
通过适配器把需要适配的类转成目标类
优点:接口可以转成自己希望的另一个接口,提高了类的复用。
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。
五、装饰者模式
作用:对已有的业务逻辑进一步的封装,使其增加额外的功能。即向一个现有的对象添加新的功能,同时又不改变其结构。
优点:可以提供比继承更多的灵活性。
缺点:多层装饰比较复杂。
六、代理模式
作用:一个类代理另一个类的功能。
模式中的角色
1. 抽象角色:声明真实对象和代理对象的共同接口。
2. 代理角色:代理对象角色内部含有对真实对象的引用,继承了抽象对象。
3. 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
缺点:由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
七、策略模式
作用:将多个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
组成:
1.抽象策略角色:通常由一个接口或者抽象类实现。
2.具体策略角色: 包装了相关的算法和行为。
3.环境角色: 持有一个策略类的引用,最终给客户端调用。
实际举例:旅行的出游方式,选择骑自行车、坐汽车,每一种旅行方式都是一个策略。
【1】简单工厂模式场景如下:
主函数
package cn.spring.library.controller;
import java.lang.reflect.InvocationTargetException;
import cn.spring.library.dao.IFactoryDao;
import cn.spring.library.factory.DaoFactory;
public class FactoryDemoTest {
public static void main(String[] args) throws Exception {
//利用工厂模式,客户端只需要对工厂指定你要创建的对象的名字(参数),工厂就可以根据你指定的参数动态
//创建不同的对象。当然,这些不同的对象就有不同的表现(在本例中通过play表示)
playMusicBox(DaoFactory.createStaticFactory("cn.*.*.impl.StaticFacotryDao001Impl"));
playMusicBox(DaoFactory.createStaticFactory("cn.*.*.impl.StaticFacotryDao002Impl"));
}
public static void playMusicBox(IFactoryDao factoryDao) {
factoryDao.saveFactory();
}
}
接口
package cn.spring.library.dao;
public interface IFactoryDao {
/**
* 注入对象
*
*/
public void saveFactory();
}
实现类001
package cn.spring.library.dao.impl;
import cn.spring.library.dao.IFactoryDao;
public class StaticFacotryDao001Impl implements IFactoryDao {
public StaticFacotryDao001Impl(){
System.out.println("这是StaticFacotryDao001Impl的构造函数,不带参数");
}
public StaticFacotryDao001Impl(String name){
System.out.println("这是StaticFacotryDao001Impl的构造函数,带参数name:"+name);
}
@Override
public void saveFactory() {
System.out.println("hello world");
}
}
实现类002
package cn.spring.library.dao.impl;
import cn.spring.library.dao.IFactoryDao;
public class StaticFacotryDao002Impl implements IFactoryDao {
public StaticFacotryDao002Impl(){
System.out.println("这是StaticFacotryDao001Impl的构造函数,不带参数");
}
public StaticFacotryDao002Impl(String name){
System.out.println("这是StaticFacotryDao001Impl的构造函数,带参数name:"+name);
}
@Override
public void saveFactory() {
System.out.println("hello world Ketty");
}
}
工厂控制器
package cn.spring.library.factory;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import cn.spring.library.dao.IFactoryDao;
import cn.spring.library.dao.impl.StaticFacotryDao002Impl;
import cn.spring.library.dao.impl.StaticFacotryDao001Impl;
public class DaoFactory {
/**
* 静态工厂
*
*/
public static final IFactoryDao getStaticFactoryDao001Impl(){
return new StaticFacotryDao001Impl();
}
public static final IFactoryDao getStaticFactoryDao002Impl(){
return new StaticFacotryDao002Impl();
}
/**
* 静态工厂控制
* 根据传递过来的不同实现类,返回不同的实例
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws SecurityException
* @throws NoSuchMethodException
*
*/
public static final IFactoryDao createStaticFactory(String name) throws Exception {
// 这边使用的是Java的Reflection机制来产生实例
// 不过客户端不用管啦
// 以后就算改变了这边的程式,客户端程式是不用更改的
//调用不带参数
//return (IFactoryDao) Class.forName(name).newInstance();
//调用带参数
Class<?> c=Class.forName(name);
Constructor<?> cst;
cst = c.getDeclaredConstructor(new Class[]{String.class});
cst.setAccessible(true);
return (IFactoryDao)cst.newInstance(new Object[]{"hansen"});
}
}
输出
这是StaticFacotryDao001Impl的构造函数,带参数name:hansen
hello world
这是StaticFacotryDao002Impl的构造函数,带参数name:hansen
hello world Ketty
分析:
工厂角色:被客户端直接调用,根据客户端指定传入的参数,动态创建客户端需要的对象
抽象产品角色:所有对象的父类(接口)
具体产品角色:即工厂的创建目标,工厂创建的对象就是这些具体类的对象。
可以看出,客户端只面对工厂,不用管产品的具体细节,客户只需向工厂要求你需要什么,其他的事情都交给工厂了。
优点:
通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了。而不必管这些对象究竟如何创建及如何组织的.明确了各
自的职责和权利。(简单地说,你到肯德基去只需要说你要鸡腿还是鸡翅就行了,不需要去管鸡腿和鸡翅是怎么做出来的,工厂为你提供了这样一个界面)
不足:
由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新
的类,则就需要改变工厂类了。
当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模
块功能的蔓延,对系统的维护和扩展非常不利;
使用场景
工厂类负责创建的对象比较少;
客户只知道传入工厂类的参数,对于如何创建对象(逻辑)不关心;
由于简单工厂很容易违反高内聚责任分配原则,因此一般只在很简单的情况下应用。
从上面的例子也可以看出来,工厂类往往是用反射机制来产生具体对象的。(因为不同类都继承自同一接口),故其扩展性很有限,产品种类必须是事先就知道的哪几种,什么时候你想要添加一个不是公共接口下的具体类就不行了。
另外,如果你不用反射机制,也不要公共接口,在工厂中使用其他逻辑(例如判断传入的字符串)来根据用户参数创建对象也行,那样扩展性也是很糟糕的,逻辑和添加只会越来多。