目录
软件设计模式概述
软件设计模式的概念与意义
软件设计模式的概念
·软件设计模式(Software Design Pattern),又称设计模式,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题,以及该问题的解决方案。也就是说,它是解决特定问题的一系列套路,是前辈们的代码设计经验的总结,具有一定的普遍性,可以反复使用。其目的是为了提高代码的可重用性、代码的可读性和代码的可靠性。
学习设计模式的意义
·设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
软件设计模式的基本要素
模式名称
·每一个模式都有自己的名字,通常用一两个词来描述,可以根据模式的问题、特点、解决方案、功能和效果来命名。模式名称(PatternName)有助于我们理解和记忆该模式,也方便我们来讨论自己的设计。
问题
·问题(Problem)描述了该模式的应用环境,即何时使用该模式。它解释了设计问题和问题存在的前因后果,以及必须满足的一系列先决条件。
解决方案
·模式问题的解决方案(Solution)包括设计的组成成分、它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象的 组合)来解决这个问题。
效果
·描述了模式的应用效果以及使用该模式应该权衡的问题,即模式的优缺点。主要是对时间和空间的衡量,以及该模式对系统的灵活性、扩充性、可移植性的影响,也考虑其实现问题。显式地列出这些效果(Consequence)对理解和评价这些模式有很大的帮助。
软件设计七大原则
开闭原则 | ·对扩展开放,对修改关闭 | 降低维护带来的新风险 |
依赖倒置原则 | ·高层不应该依赖低层,要面向接口编程 | 更利于代码结构的升级扩展 |
单一职责原则 | ·一个类只干一件事,实现类要单一 | 便于理解,提高代码的可读性 |
接口隔离原则 | ·一个接口只干一件事,接口要精简单一 | 功能解耦,高聚合、低耦合 |
迪米特法则 | ·不该知道的不要知道,一个类应该保持对其它对象最少的了解,降低耦合度 |
只和朋友交流,不和陌生人说话,减少代码臃肿 |
里氏替换原则 | ·不要破坏继承体系,子类重写方法功能发生改变,不应该影响父类方法的含义 | 防止继承泛滥 |
合成复用原则 | ·尽量使用组合或者聚合关系实现代码复用,少使用继承 |
降低代码耦合 |
单例模式(单例设计模式)
单例模式的定义与特点
定义
·单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
·在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。
特点
·1. 单例类只有一个实例对象;
·2. 该单例对象必须由单例类自行创建;
·3. 单例类对外提供一个访问该单例的全局访问点。
单例模式的优点和缺点
优点
·单例模式可以保证内存里只有一个实例,减少了内存的开销。
·可以避免对资源的多重占用。
·单例模式设置全局访问点,可以优化和共享资源的访问。
缺点
·单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
·在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
注意:单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
单例模式的应用场景
·需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
·某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
·某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
·某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
·频繁访问数据库或文件的对象。
·对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
·当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接
单例模式的结构与实现
单例模式的结构
·单例类:包含一个实例且能自行创建这个实例的类。
·访问类:使用单例的类。
单例模式的实现
懒汉式单例
·该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例
public class LazySingleton {
private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步
private LazySingleton() {
} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
·注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
饿汉式单例
·该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
注意:饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。
枚举类单例
·其他单例实现都有可能遭到反射的破坏,枚举类不会,更加安全
public enum HungrySingleton {
INSTANCE;
public HungrySingleton getInstance(){
return INSTANCE;
}
}
抽象工厂模式
模式的定义与特点
定义
·抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
特点
·使用抽象工厂模式一般要满足以下条件。
·可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
·当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
·抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
·其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度
模式的结构与实现
模式的结构
抽象工厂模式的主要角色如下:
·1. 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
·2. 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
·3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
·4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
模式的实现
ProductFactory
public interface ProductFactory {
IphoneProduct productphone();
IrouterProduct productrouter();
}
IphoneProduct
public interface IphoneProduct {
void start();
void close();
void call();
void sendmessage();
}
IrouterProduct
public interface IrouterProduct {
void start();
void close();
void setting();
}
HuaweiFactory
public class HuaweiFactory implements ProductFactory{
@Override
public IphoneProduct productphone() {
return new HuaweiPhone();
}
@Override
public IrouterProduct productrouter() {
return new HuaweiRouter();
}
}
HuaweiPhone
public class HuaweiPhone implements IphoneProduct{
@Override
public void start() {
System.out.println("华为手机开机!");
}
@Override
public void close() {
System.out.println("华为手机关机!");
}
@Override
public void call() {
System.out.println("华为手机打电话!");
}
@Override
public void sendmessage() {
System.out.println("华为手机发信息!");
}
}
HuaweiRouter
public class HuaweiRouter implements IrouterProduct{
@Override
public void start() {
System.out.println("打开华为路由器!");
}
@Override
public void close() {
System.out.println("关闭华为路由器!");
}
@Override
public void setting() {
System.out.println("设置华为路由器!");
}
}
XiaomiFactory
public class XiaomiFactory implements ProductFactory{
@Override
public IphoneProduct productphone() {
return new XiaomiPhone();
}
@Override
public IrouterProduct productrouter() {
return new XiaomiRouter();
}
}
XiaomiPhone
public class XiaomiPhone implements IphoneProduct{
@Override
public void start() {
System.out.println("小米手机开机!");
}
@Override
public void close() {
System.out.println("小米手机关机!");
}
@Override
public void call() {
System.out.println("小米手机打电话!");
}
@Override
public void sendmessage() {
System.out.println("小米手机发信息!");
}
}
XiaomiRouter
public class XiaomiRouter implements IrouterProduct{
@Override
public void start() {
System.out.println("打开小米路由器!");
}
@Override
public void close() {
System.out.println("关闭小米路由器!");
}
@Override
public void setting() {
System.out.println("设置小米路由器!");
}
}
Client
public class Client {
public static void main(String[] args) {
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct productphone = xiaomiFactory.productphone();
productphone.start();
productphone.call();
XiaomiFactory xiaomiFactory1 = new XiaomiFactory();
IrouterProduct productrouter1 = xiaomiFactory1.productrouter();
productrouter1.start();
productrouter1.close();
HuaweiFactory huaweiFactory = new HuaweiFactory();
IrouterProduct productrouter = huaweiFactory.productrouter();
productrouter.start();
productrouter.setting();
}
}
代理模式(代理设计模式)
代理模式的定义与特点
定义
·代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介
缺点
·静态代理模式的主要缺点有:
·代理模式会造成系统设计中类的数量增加
·在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
·增加了系统的复杂度;
·用动态代理方式可解决上述问题
代理模式的结构与实现
模式的结构
模式的实现
静态代理
UserService
public interface UserService {
void add();
void delete();
void update();
void query();
}
UserServiceImp
public class UserServiceImp implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
ProxyUser
public class ProxyUser implements UserService{
private UserServiceImp userServiceImp;
public void setUserServiceImp(UserServiceImp userServiceImp) {
this.userServiceImp = userServiceImp;
}
public void add(){
log("add");
userServiceImp.add();
}
public void delete(){
log("delete");
userServiceImp.delete();
}
public void update(){
log("update");
userServiceImp.update();
}
public void query(){
log("query");
userServiceImp.query();
}
public void log(String msg){
System.out.println("使用了"+msg+"方法");
}
}
Client
public class Client {
public static void main(String[] args) {
UserServiceImp userServiceImp = new UserServiceImp();
ProxyUser proxyUser = new ProxyUser();
proxyUser.setUserServiceImp(userServiceImp);
proxyUser.add();
}
}
动态代理
UserService
public interface UserService {
void add();
void delete();
void update();
void query();
}
UserServiceImp
public class UserServiceImp implements UserService {
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
ProxyInvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object res = method.invoke(target, args);
return res;
}
public void log(String msg){
System.out.println("进行了"+msg+"操作");
}
}
Client
public class Client {
public static void main(String[] args) {
UserServiceImp userServiceImp = new UserServiceImp();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();
proxyInvocationHandler.setTarget(userServiceImp);
UserService proxy = (UserService) proxyInvocationHandler.getProxy();
proxy.delete();
}
}