java小萌新的博客
java 设计模式 (一)
初始设计模式
了解设计模式的概念
是一套反复使用, 多人知晓, 经过分类编目的优秀代码设计经验的总结.
提高代码: 重用性 易理解, 可靠性
面向对象程序软件系统一般分两大类: 程序工具箱 框架 (framework)
了解设计模式的历史
源于建筑工程设计大师 Christopher Alexander( 克里斯托弗·亚历山大)
1995年Gof的<>
理解设计模式的要素
模式名称
简洁地描述设计模式的问题, 解决方案,和效果0
问题
描述了应该在何时使用模式
环境
模式的使用范围, 模式应用前的起始条件(前提条件)
解决方案
描述设计的组成成分, 以及他们之间的相互关系及各自的职责和协调方式
效果
描述了模式应用的效果和使用模式应该权衡的问题
举例
举例使用一个或者多个示意性的应用来说明特定的真实环境
末态环境
模式应用到系统之后的状态
是末态条件和可能有的副作用
推理
推理解析模式的步骤, 规则, 以及模式作为一个整体是如何以特定的方式解决模式.
可以让使用者知道模式是如何工作, 为什么可以工作以及此模式的优缺点
模式的解答 描述模式外部可见的结构和行为
推理 给出模式在系统表层一下的深层次和关键机制
其他有关模式
描述现有的系统中某种模式与其他模式的静态或者动态的关系
已知应用
已有系统中出现或者应用的模式例子
掌握设计模式的分类
三大设计模式 (23种模式)
创建型模式
单例模式,工厂方法模式,抽象工厂模式,建造者模式,原型模式
结构型模式
代理模式,装饰模式,适配器模式, 组合模式,桥梁模式,外观模式,享元模式
行为型模式
模板方法模式,命令模式,责任链模式,策略模式,迭代器模式
中介者模式,观察者模式,备忘录模式,访问者模式,状态模式,解析器模式
六大原则
单一职责原则
一个类,应当只有一个引起它变化的原因,即一个类应该只有一个职责
里氏替换原则
- 如果一个类型为 S 的对象 O1 ,都有类型为 T 的对象 O2 ,使得 S 的定义的所有程序 p 中所有程序 P 中所有的对象 O1 都替换成 O2 时, 程序 P 的行为没有发生改变,那么类型 T 是类型 S 的子类。
- 所有引用基类的地方必须能透明地使用其子类对象。
依赖倒置原则
- 高层模块不应该依赖底层模块,两字都依赖其抽象。
- 抽象不依赖细节
- 细节应该以来抽象
迪米特原则
- 只与你直接的朋友通信。
- 不要跟“陌生人”说话。
- 每一个软件单位对其他的单位都只有最少的了解,这些了解仅局限于那些与本单位密切相关的软件单位。
接口隔离原则
- 客户端不应该依赖它不需要的接口。
- 类间的依赖关系应该建立在最小接口之上。
开闭原则
一个软件实体应该对扩展开放,对修改关闭
六大原则
单一原则
SRP simple Responsibility Principe
一个类,应当只有一个引起它变化的原因,即一个类应该只有一个职责
降低类的复杂性
提高类的可读性
提高代码的可维护性和复用性
降低变更而引起的风险
单一原则提出一种编写程序的标准:用“职责”或者“变化原因”来衡量接口或者类的设计是否优良,但“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。
实例
以用户管理为例,业务逻辑UserService, 数据访问层UserDao, 实体对象层User,每个类具有不用职责
//实体对象
@Data
@AllArgsConstructor
public class SysUser {
private Long id;
private String username;
private String tel;
private String password;
private String salt;
private String nickname;
private BigDecimal score;
private Integer serverNum;
private Long createTime;
private Long updateTime;
private String createBy;
private String updateBy;
}
//业务逻辑层
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public SysUser findByUserName(String username) {
return sysUserMapper.findByUsername(username);
}
}
//数据访问层
public interface SysUserMapper {
SysUser findByUsername(String username);
}
Java
Copy
里氏替换原则
LSP liskov Substitution Principe
- 如果一个类型为 S 的对象 O1 ,都有类型为 T 的对象 O2 ,使得 S 的定义的所有程序 p 中所有程序 P 中所有的对象 O1 都替换成 O2 时, 程序 P 的行为没有发生改变,那么类型 T 是类型 S 的子类。
- 所有引用基类的地方必须能透明地使用其子类对象
在面向对象的语言中,继承是必不可少、优秀的语言机制,他的主要优点如下:
相应的,继承也存在弱点,主要以下几个方面:
- 继承是入侵式,只要继承,就必须拥有父类的所有属性和方法。
- 降低代码的灵活性,子类必须拥有父类的属性和方法,使子类收到限制。
- 增强了耦合性。当父类的常量,变量和方法修改时,必须考虑子类的修改,这种修改可能造成大片的代码需要重构
从整体上看,继承是利大于弊,然而如何让继承中的利的因素发挥最大的作用,同时减少弊所带来的麻烦,这就需要应用“里氏替换原则”。
在编译期,java语言编译器会检测一个程序是否符合里氏替换原则,这是一个无关实现、纯语法意义上的检查。
里氏替换原则的规范
子类必须完全实现父类的方法。
子类可以有自己的个性。
子类覆盖或者实现父类的方法时输入参数可以被放大。
子类覆盖或者实现父类方法时输出结果可以被缩小。
体现
策略模式
组合模式
代理模式
实例
public abstract class Animal {
public abstract void eat();
}
//-----------------------------
public class Bird extends Animal {
@Override
public void eat() {
System.out.println("鸟吃虫");
}
}
//---------------------------
public class Horse extends Animal {
@Override
public void eat() {
System.out.println("马吃草");
}
}
//---------------------------------
public class Main {
public static void main(String[] args) {
Animal animal;
animal = new Horse();
animal.eat();
animal = new Bird();
animal.eat();
//Horse horse = new Animal();错误
}
}
Java
Copy
依赖倒置原则
DIP Dependence Inversion Principe
- 高层模块不应该依赖底层模块,两字都依赖其抽象。
- 抽象不依赖细节
- 细节应该以来抽象
传统的过程性系统的设计系统的设计方法倾向于高层次的模块依赖于低层次的模块,抽象层次依赖于具体层次。“倒置原则”将这个错误的依赖关系倒置过来。(抽象就是接口和抽象类
)。
依赖倒置原则在java的表现:
- 模块间的依赖通过抽象产生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生。
- 接口或抽象类不依赖于实现类,实现类依赖于接口或者抽象类。
依赖倒置原则更加精确的定义就是“面向接口编程”--OOD(Object-Oriented Design)的精髓。依赖倒置原则可以减少类之间的耦合性,提高系统的稳定性,降低并行开发的风险,并提高代码的可读性和可维护性。依赖倒置是JavaBean、EJB和COM等组件设计模型背后的基本原则。
使用原则
- 每个类应该都具有接口或者抽象类,或者同时具备抽象类和接口,这是依赖倒置的基本要求。
- 变量的表面类型尽量是接口或者抽象类。
- 任何类都不应该从具体类派生。
- 尽量不要重写基类的方法。
依赖倒置在小型项目中很难体现出来。
依赖倒置原则是六种设计原则最难以实现,他是实现开闭原则的重要途径。
实例
当司机想换车只需要修改“new BMW()”或者“new Beanz()”, 降低变更子类引起的风险。
public interface ICar {
void run();
}
//----------------------------------
public class Beanz implements ICar {
@Override
public void run() {
System.out.println("奔驰汽车在行驶...");
}
}
//------------------------------------------
public class BMW implements ICar {
@Override
public void run() {
System.out.println("宝马汽车在行驶...");
}
}
//------------------------------------------------
public interface IDriver {
void driver(ICar iCar);
}
//-----------------------------------------------
public class Driver implements IDriver {
@Override
public void driver(ICar iCar) {
iCar.run();
}
}
//-------------------------------------
public class Test {
public static void main(String[] args) {
IDriver tom = new Driver();
//tom开奔驰
ICar iCar = new Beanz();
tom.driver(iCar);
//tom开宝马
iCar = new BMW();
tom.driver(iCar);
}
}
//奔驰汽车在行驶...
//宝马汽车在行驶...
Java
Copy
接口隔离原则
ISP Interface Segregation Principe
- 客户端不应该依赖它不需要的接口。
- 类间的依赖关系应该建立在最小接口之上。
具体含义
- 一个类对另一个类的依赖应当是建立在最小接口上。
- 一个接口代表一个角色,不应该将不同的角色都交给一个接口。
- 不应该强迫客户依赖于他们不用的接口。
实例
电子商务系统,该系统有订单这个类,并在三个地方会使用到订单。
- 门户,只有查询方法。
- 外部系统,有添加订单的方法。
- 管理后台,添加,删除,修改,查询。
通过三个IOrderForAdmin , IOrderForOtherSys , IOrderForPortal对不同用户使用的接口进行隔离.
接口隔离原则是对接口的定义, 同时对类的定义, 应尽量使用原子接口或者原子类,其中"原子"在实践应用中可以根据以下几条标准来衡量 :
- 一个接口支队一个子模块或者业务逻辑进行服务.
- 只保留接口中业务逻辑需要的public方法,
- 尽量修改污染的接口, 若修改的风险较大,则可采用适配器模式进行转化处理.
- 接口设计应因项目而异, 因环境而异不能教条照搬.
设计接口时应根据经验和常识决定接口的粒度大小, 粒度太小, 将会导致接口数量剧增, 给开发带来难度; 如果接口粒度太大, 灵活性降低, 将无法提供定制服务, 给项目带来无法估计的风险.
//不同对象 权限不同
public interface IOrderForAdmin {
public String getOrder();
public void insertOrder();
public void updateOrder();
public void deleteOrder();
}
//-------------------------------------------
public interface IOrderForOtherSys {
public void insertOrder();
}
//-------------------------------------------------
public interface IOrderForPortal {
public String getOrder();
}
//----------------------------------------------
//订单
public class Order implements IOrderForAdmin, IOrderForOtherSys, IOrderForPortal {
public static IOrderForPortal getIOrderForPortal() {
return new Order();
}
public static IOrderForOtherSys getIOrderForOtherSys() {
return new Order();
}
public static IOrderForAdmin getIOrderForAdmin() {
return new Order();
}
@Override
public String getOrder() {
return "返回订单";
}
@Override
public void insertOrder() {
System.out.println("插入订单");
}
@Override
public void updateOrder() {
System.out.println("修改订单");
}
@Override
public void deleteOrder() {
System.out.println("删除订单");
}
}
//-------------------------------------------
public class Test {
public static void main(String[] args) {
//获取用户门户接口对应的实例,该对象只能使用getOrder();
IOrderForPortal op = Order.getIOrderForPortal();
System.out.println(op.getOrder());
//获取外部系统门户接口对应的实例,该对象只能使用getOrder();
IOrderForOtherSys os = Order.getIOrderForOtherSys();
os.insertOrder();
//获取管理平台接口对应的实例,该对象只能使用getOrder();
IOrderForAdmin oa = Order.getIOrderForAdmin();
System.out.println(oa.getOrder());
oa.insertOrder();
oa.updateOrder();
oa.deleteOrder();
}
}
//---------------------------------------------------
//返回订单
//插入订单
//返回订单
//插入订单
//修改订单
//删除订单
Java
Copy
迪米特法则
LKPLeast Knowledge Principe
迪米特法则又称最小知识原则
()
- 只与你直接的朋友通信。
- 不要跟“陌生人”说话。
- 每一个软件单位对其他的单位都只有最少的了解,这些了解仅局限于那些与本单位密切相关的软件单位。
例子
someone不能喝陌生人直接作用
可以通过Friend间接与comeone通信
package com.gylang.design.pattern.lkp;
/**
* @author gylang,
* @version 1.0
* @date 2020/1/3,
*/
public class Main {
public static void main(String[] args) {
SomeOne someOne = new SomeOne();
someOne.call(new Friend());
}
}
class SomeOne{
public void call(Friend friend){
friend.forward();
};
}
class Friend{
private Stranger stranger = new Stranger();
public void forward() {
stranger.strangerMethod();;
}
public void friendMethod() {
System.out.println("这是朋友方法");
}
}
class Stranger{
public void strangerMethod() {
System.out.println("这是陌生人方法");
}
}
Java
Copy
开闭原则
OCP Open-Closed Principe
一个软件实体应该对扩展开放,对修改关闭
开闭原则是最基础的原子, 起到总指挥作用, 其他原则都是开闭原则的具体形态, 即其他原则都是开闭原则的手段和工具.
-
开闭原则提高复用性.
开闭原则的设计保证系统是在一个高层次上面实现复用的系统.
-
开闭原则提高可维护性.
对已有软件模块, 特别是最重要的抽象层模块不能对其进行修改, 这使变化中的软件系统有一定的稳定性和延续性, 便于系统维护.
-
开闭原则提高灵活性.
开闭原则可以通过扩展已有的软件系统, 提供新的行为, 快速应对变化, 以满足对软件的新需求, 使变化中软件系统有一定的适应性和灵活性.
-
开闭原则易于测试.
开闭原则通过扩展实现业务逻辑的变化, 而不是修改, 因此, 对于新增加的类, 只需要增加相应的测试类, 编写对应的测试方法, 只需要保证新增加的类是正确的就可以了.
package com.gylang.design.pattern.openclosed;
import java.util.ArrayList;
/**
* @author gylang,
* @version 1.0
* @date 2020/1/3,
*/
public class Main {
public static void main(String[] args) {
BookStore bookStore = new BookStore();
bookStore.showBooks();
}
}
interface Ibook {
public String getName();
public double getPrice();
public String getAuthor();
}
class NovelBook implements Ibook {
private String name;
private double price;
private String author;
public NovelBook(String name, double price, String author) {
this.name = name;
this.price = price;
this.author = author;
}
@Override
public String getName() {
return name;
}
@Override
public double getPrice() {
return price;
}
@Override
public String getAuthor() {
return author;
}
}
class BookStore {
private ArrayList<Ibook> ibookList = new ArrayList<>();
public BookStore (){
ibookList.add(new NovelBook("西游记", 79.25, "吴承恩"));
ibookList.add(new NovelBook("红楼梦", 55.25, "曹雪芹"));
ibookList.add(new NovelBook("水浒传", 37.21, "斯耐庵"));
ibookList.add(new NovelBook("三国演义", 53.21, "罗贯中"));
}
public void showBooks() {
System.out.println("---------------书店预售书本---------------------");
System.out.println("书名\t\t价格\t\t作者\t\t");
for (Ibook ibook : ibookList) {
System.out.println(ibook.getName()+ "\t\t¥"+ibook.getPrice()+"元\t\t"+ibook.getAuthor()+"\t\t");
}
}
}
Java
Copy
扩展
package com.gylang.design.pattern.openclosed;
import java.util.ArrayList;
/**
* @author gylang,
* @version 1.0
* @date 2020/1/3,
*/
public class Main {
public static void main(String[] args) {
BookStore bookStore = new BookStore();
bookStore.showBooks();
}
}
class BookStore {
private ArrayList<Ibook> ibookList = new ArrayList<>();
public BookStore (){
ibookList.add(new OffNovelBook("西游记", 79.25, "吴承恩"));
ibookList.add(new OffNovelBook("红楼梦", 55.25, "曹雪芹"));
ibookList.add(new OffNovelBook("水浒传", 37.21, "斯耐庵"));
ibookList.add(new OffNovelBook("三国演义", 53.21, "罗贯中"));
}
public void showBooks() {
System.out.println("---------------书店预售书本---------------------");
System.out.println("书名\t\t价格\t\t作者\t\t");
for (Ibook ibook : ibookList) {
System.out.println(ibook.getName()+ "\t\t¥"+ibook.getPrice()+"元\t\t"+ibook.getAuthor()+"\t\t");
}
}
}
class OffNovelBook extends NovelBook {
public OffNovelBook(String name, double price, String author) {
super(name, price, author);
}
@Override
public String getName() {
return super.getName();
}
@Override
public double getPrice() {
return super.getPrice() * 0.9;
}
@Override
public String getAuthor() {
return super.getAuthor();
}
}