面向对象与设计原则和23种设计模式

https://www.cnblogs.com/az4215/p/11481076.html

面向对象建模:
封装
包装一下不要让别人知道或者看到的东西,保护好自己的隐私。
继承
继承就是遗传,子类继承了父类的一些属性和方法(功能)。
多态
使用指向父类的指针或者引用,能够调用子类的对象。

面向对象的分析:
一、需求模型
5W1H8C:

5W
When 时间 Where 地点 Who 参与人,不一定是“人”,也可以系统,第三方组织机构 What 想要的输出,一个文档一个报告一张图片一个系统等 Why 客户遇到的问题、困难、阻碍也是客户提出的需求的驱动力。

1H
How 用来描述整个流程如何运作的,一般采用用例方法进行流程性(Normal Exception Alternative)描述。

8C
Performance 响应时间、吞吐量 ;Cost 实现系统而需要付出的代价;Time 交付的时间;Reliability RPO RTO;Security 对信息安全的保护能力;Compliance 满足行业的标准、法律法规 规范;Technology 客户要求使用的某种技术;Compatibility 与其它系统相互与兼容能力。

还需要考虑用例方法(NEA)
Normal 正常流程下的运行情况
Exception 异常流程下怎么处理
Alternative 替换流程:异常或其他情况下的替换处理

功能属性
质量属性
用例分析
正常
异常
替换

二、领域模型
领域分析
名词
属性
关系

在需求模型中去 找名词
对领域模型中的名词 加属性
对领域模型中的名词 建关系

三、设计模型
静态模型
领域类映射
设计原则和模式
辅助类设计
动态模型

设计模型主要包含两部分内容:静态模型和动态模型。
静态模型:描述系统包含的类,类的名称、职责、属性、方法,以及类与类之间的关系,主要用于指导类的声明。
动态模型:描述系统的“动态”行为,描述类本身的一些动作或者状态变化以及类之间如何配合以完成最终的业务功能。主要用于每个方法内部的具体实现过程。

领域类映射:设计模型需要将领域模型中的领域对象,筛选出无关的业务领域类,并通过名称映射、属性映射、提炼方法(在需求用例中找动词,需要筛选出与领域类无关的动词,提炼出发出动作的对象并分配给相关的领域类上)。

四、实现模型
语言实现
C++
Java

五、设计原则:
高内聚,低耦合
内聚:一个模块内部元素彼此结合的紧密程度(凝聚力),主要观注模块的元素是否都忠于模块的职责,简单来说就是“不要挂羊头卖狗肉”。

耦合:程序模块之间相互依赖的程度。
无耦合(谁也不依赖,但并不代表是优秀的设计)

高内聚,低耦合是互相冲突的,它们就像天平的两端,不能同时做到都往上升,真正好的设计是在高内聚和低耦合之间进行平衡。

设计原则:
单一职责原则SRP(Single Responsibility Principle) :一个类应该有且只有一个变化的原因。
为什么将不同的职责分离到单独的类中是如此的重要呢?
因为每一个职责都是一个变化的中心。当需求变化时,这个变化将通过更改职责相关的类来体现。
如果一个类拥有多于一个的职责,则这些职责就耦合到在了一起,那么就会有多于一个原因来导致这个类的变化。对于某一职责的更改可能会损害类满足其他耦合职责的能力。这样职责的耦合会导致设计的脆弱,以至于当职责发生更改时产生无法预期的破坏。

开放封闭原则OCP(Open-Closed Principle):软件实体(类、模块、函数等)应对扩展开放,但对修改封闭。

当一个需求变化导致程序中多个依赖模块都发生了级联的改动,那么这个程序就展现出了我们所说的 “坏设计(bad design)” 的特质。应用程序也相应地变得脆弱、僵化、无法预期和无法重用。开放封闭原则(Open Closed Principle)即为解决这些问题而产生,它强调的是你设计的模块应该从不改变。当需求变化时,你可以通过添加新的代码来扩展这个模块的行为,而不去更改那些已经存在的可以工作的代码。

里氏替换原则LSP(Liskov Substitution Principle)
使用基类对象指针或引用的函数必须能够在不了解衍生类的条件下使用衍生类的对象。

开放封闭原则(Open Closed Principle)是构建可维护性和可重用性代码的基础。它强调设计良好的代码可以不通过修改而扩展,新的功能通过添加新的代码来实现,而不需要更改已有的可工作的代码。抽象(Abstraction)和多态(Polymorphism)是实现这一原则的主要机制,而继承(Inheritance)则是实现抽象和多态的主要方法。
那么是什么设计规则在保证对继承的使用呢?优秀的继承层级设计都有哪些特征呢?是什么在诱使我们构建了不符合开放封闭原则的层级结构呢?

接口隔离原则ISP(Interface Segregation Principle)
当客户类被强迫依赖那些它们不需要的接口时,则这些客户类不得不受制于这些接口。这无意间就导致了所有客户类之间的耦合。换句话说,如果一个客户类依赖了一个类,这个类包含了客户类不需要的接口,但这些接口是其他客户类所需要的,那么当其他客户类要求修改这个类时,这个修改也将影响这个客户类。通常我们都是在尽可能的避免这种耦合,所以我们需要竭尽全力地分离这些接口。

接口分离原则(Interface Segregation Principle)用于处理胖接口(fat interface)所带来的问题。如果类的接口定义暴露了过多的行为,则说明这个类的接口定义内聚程度不够好。换句话说,类的接口可以被分解为多组功能函数的组合,每一组都服务于不同的客户类,而不同的客户类可以选择使用不同的功能分组。
ISP 原则承认了对象设计中非内聚接口的存在。但它建议客户类不应该只通过一个单独的类来使用这些接口。取而代之的是,客户类应该通过不同的抽象基类来使用那些内聚的接口。在不同的编程语言中,这里所指的抽象基类可以指 “接口(interface)”、“协议(protocol)”、“签名(signature)” 等。

依赖倒置原则DIP(Dependency Inversion Principle)
那到底是什么让设计变得僵化、脆弱和难以复用呢?答案是模块间的相互依赖。
如果一个设计不能很容易被修改,则设计就是僵化的。这种僵化性体现在,如果对相互依赖严重的软件做一处改动,将会导致所有依赖的模块发生级联式的修改。当设计师或代码维护者无法预期这种级联式的修改所产生的影响时,那么这种蔓延的结果也就无法估计了。这导致软件变更的代价无法被准确的预测。而管理人员在面对这种无法预测的情况时,通常是不会对变更进行授权,然后僵化的设计也就得到了官方的保护。
脆弱性是指一处变更将破坏程序中多个位置的功能。而通常新产生的问题所涉及的模块与该变更所涉及的模块在概念上并没有直接的关联关系。这种脆弱性极大地削弱了设计与维护团队对软件的信任度。同时软件使用者和管理人员都不能预测产品的质量,因为对应用程序某一部分简单的修改导致了其他多个位置的错误,而且看起来还是完全无关的位置。而解决这些问题将可能导致更多的问题,使得维护过程陷进了 “狗咬尾巴” 的怪圈。
如果设计中实现需求的部分对一些与该需求无关的部分产生了很强的依赖,则该设计陷入了死板区域。设计师可能会被要求去调查是否能够将该设计应用到不同的应用程序,要能够预知该设计在新的应用中是否可以完好的工作。然而,如果设计的模块间是高度依赖的,而从一个功能模块中隔离另一个功能模块的工作量足以吓到设计师时,设计师就会放弃这种重用,因为隔离重用的代价已经高于重新设计的代价。

类 “Copy” 既没有依赖 “Keyboard Reader” 也没有依赖 “Printer Writer”。因此,这些依赖已经被反转了(Inverted)。“Copy” 类依赖于抽象,而真正的 “Reader” 和 “Writer” 的具体实现也依赖于抽象。
此时,我们就可以重用 “Copy” 类,而不需要具体的 “Keyboard Reader” 和 “Printer Writer”。我们可以通过创造新的 “Reader” 和 “Writer” 衍生类然后替换到 “Copy” 中。而且,无论有多少种 “Reader” 和 “Writer” 被创建,“Copy” 都不会依赖于它们。因为没有这些模块间的相互依赖,也使得程序不会变的僵化和脆弱。并且 “Copy” 类也可以被复用到多种不同的情况中。它不再是固定的。

最少知识原则LKP(Least Knowledge Principle):
最少知识原则(Least Knowledge Principle),或者称迪米特法则(Law of Demeter),指更好的信息隐藏和更少的信息重载。

组合优先继承
尽量使用对象组合,而不是继承来达到复用的目的,在面向设计过程中对于关联关系首先要通过组合/聚合关系来实现复用功能,其次才是通过继承和实现关系来实现复用。
迪米特法则
一个对象应该对其他对象保持最少的了解。一个对象只需要保持对其朋友对象的关注就好,其他的交给中介类!

设计模式:
Prototype(原型):
别名

Clone
意图

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
在这里插入图片描述
参与者

Prototype

声明一个克隆自身的接口。
ConcretePrototype

实现一个克隆自身的操作。
Client

让一个原型克隆自身从而创建一个新的对象。

适用性

在以下情况下可以使用 Prototype 模式:

一个系统要独立于它的产品的创建、构成和表示时。
当要实例化的类是在运行时刻指定时,例如:通过动态装载。
为了避免创建一个与产品类层次平行的工厂类层次时。
当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

Factory Method(工厂方法):
实现
实现方式(一):Creator 类是一个抽象类并且不提供它所声明的工厂方法的实现。
实现方式(二):Creator 类是一个具体类而且为工厂方法提供一个缺省的实现。
实现方式(三):参数化工厂方法。
实现方式(四):使用模板以避免创建子类。
别名

虚构造器 (Virtual Constructor)
意图

定义一个用于创建目标对象的接口,让子类决定实例化哪一个目标类。

Factory Method 使一个类的实例化延迟到其子类。

Define an interface for creating an object, but let subclasses decide which class to instantiate.

Factory Method lets a class defer instantiation to subclasses.

在这里插入图片描述
参与者

Product

定义工厂方法所创建的对象的接口(Interface)。
ConcreteProduct

实现 Product 接口。
Creator

声明工厂方法,该方法返回一个 Product 类型的对象。 Creator 也可以定义一个工厂方法的缺省实现,它返回一个缺省的 ConcreteProduct 对象。
可以调用工厂方法以创建一个 Product 对象。
ConcreteCreator

重定义(Override)工厂方法以创建一个 Product 对象。

适用性

在下列情况下可以使用 Factory Method 模式:

当一个类不知道它所必须创建的对象的类的时候。
当一个类希望由它的子类来指定它所创建的对象的时候。
当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

设计模式-责任链模式(responsibility)
角色和职责:

1.抽象处理者(Handler)-CarHandler:
2.具体处理者(Concrete Handler) -:

在这里插入图片描述

public class Main {
    public static void main(String[] args) {
        CarHandler headerHandler = new HeaderHandler();//车头
        CarHandler bodyHandler = new BodyHandler();//车身
        CarHandler footerHandler = new FooterHandler();//车尾

        headerHandler.setChaimHandler(bodyHandler).setChaimHandler(footerHandler);
        headerHandler.task();//拼装车
    }
}

/**
 * 汽车类
 */
public abstract class CarHandler {
    protected CarHandler carHandler;

    public  CarHandler setChaimHandler(CarHandler carHandler){
        this.carHandler = carHandler;
        return carHandler;
    }
    public abstract void task();
}

/**
 * 安装车头
 */
public class HeaderHandler extends CarHandler{
    @Override
    public void task() {
        System.out.println("安装车头!");
        if(carHandler != null){
            carHandler.task();
        }
    }
}

/**
 * 安装车身
 */
public class BodyHandler extends CarHandler{
    @Override
    public void task() {
        System.out.println("安装车身");
        if(carHandler != null){
            carHandler.task();
        }
    }
}

/**
 * 安装车尾
 */
public class FooterHandler extends CarHandler{
    @Override
    public void task() {
        System.out.println("安装车尾");
        if(carHandler != null){
            carHandler.task();
        }
    }
}

优缺点:

优:1.责任的分担,每个类只需要处理自己该处理的工作,明确各类的责任范围,符合类的最小封装原则;

   2.可以根据需要自由组合工作流程。

   3.类和类之间松耦合

缺:因为处理时以链的形式在对象间传递消息,根据实现方式不同,可能影响处理速度

应用场景:

1.比如客户要完成一个任务,任务包括a,b,c,d四个部分。首先把任务交给a,a完成后交给b,b完成后交给c,c完成交给d

2.政府某项工作,县政府完成自己能处理的部分,不能处理的交给市政府。市政府处理自己能处理的部分,不能处理的交给省政府。

3.软件窗口的消息传递

4.过滤器filter的实现。
优缺点:

优:1.责任的分担,每个类只需要处理自己该处理的工作,明确各类的责任范围,符合类的最小封装原则;

   2.可以根据需要自由组合工作流程。

   3.类和类之间松耦合

缺:因为处理时以链的形式在对象间传递消息,根据实现方式不同,可能影响处理速度

应用场景:

1.比如客户要完成一个任务,任务包括a,b,c,d四个部分。首先把任务交给a,a完成后交给b,b完成后交给c,c完成交给d

2.政府某项工作,县政府完成自己能处理的部分,不能处理的交给市政府。市政府处理自己能处理的部分,不能处理的交给省政府。

3.软件窗口的消息传递

4.过滤器filter的实现。

Decorator(装饰):

实现
实现方式(一):Decorator 对象的接口必须与它所装饰的 Component 的接口保持一致。
实现方式(二):省略抽象的 Decorator 类。
别名

包装器(Wrapper)
意图

动态地给一个对象添加一些额外的职责。

就增加功能来说,Decorator 模式相比生成子类更为灵活。

在这里插入图片描述

参与者

Component

定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent

定义一个对象,可以给这个对象添加一些职责。
Decorator

维持一个指向 Component 对象的指针,并定义一个与 Component 接口一致的接口。
ConcreteDecorator

向组件添加职责。

适用性

在以下情况下可以使用 Decorator 模式:

在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤销的职责。
当不能采用生成子类的方法进行扩充时。
Adapter(适配器):

别名

包装器(Wrapper)
意图

将一个类的接口转换成客户希望的另外一个接口。

Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

在这里插入图片描述
参与者

Target

定义 Client 使用的与特定领域相关的接口。
Client

与符合 Target 接口的对象协同。
Adaptee

定义一个已经存在的接口,这个接口需要适配。
Adapter

对 Adaptee 的接口与 Target 接口进行适配。

适用性

在以下情况下可以使用 Adapter 模式:

你想使用一个已经存在的类,而它的接口不符合你的需求。
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类协同工作。
你想使用一些已经存在的类,但是不可能对每一个都进行子类化匹配它们的接口。对象适配器可以适配它的父类的接口。

外观(Facade)模式:
外观模式是一种非常简单的模式,简单到我们经常都会使用,比如对于类A和B,如果两者需要交互,经过一定的处理过程才能实现某一个具体的功能,那么我们可以将这个处理的过程定义为一个新的类,然后在这个类里面将类A和B的处理步骤整合在一起,对于外界我们只暴露新的类中的这个接口,这样代码的复用性就非常的好了,可以将这些代码作为组件去让其他程序去使用,这在我们的开发之中是非常常见的。甚至我们可以将抽象工厂模式中创建产品的接口当做外观模式的一种应用,这也是一种整合。对于模板方法,其实我们也是在父类之中面向抽象编程的,将一些关系整合起来,不过差别还是非常明显的,在外观模式中没有继承关系,是新建一个类来整合其它类之间复杂的相互依赖,调用等关系,因此外观模式比较直观。

在这里插入图片描述
观察者(Observer)模式:
定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。
理解:类似于报纸订阅,向某家报社(被观察者)订阅报纸,只要有新报纸出版,他就会把新报纸送到订阅者(观察者)手中。同时订阅者可以取消订阅这份报纸。

Template模板模式:
  定义一个操作中算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。

通俗点的理解就是 :完成一件事情,有固定的数个步骤,但是每个步骤根据对象的不同,而实现细节不同;就可以在父类中定义一个完成该事情的总方法,按照完成事件需要的步骤去调用其每个步骤的实现方法。每个步骤的具体实现,由子类完成。

代理(Proxy)设计模式:
代理模式或许我们都听说过,至少知道代理(Proxy)这个东西的,否则看这篇博客也没任何意义的。什么叫做代理,代理是代替服务器去接受请求者的请求的中间人。

在这里插入图片描述

Main类:


```java
  1 package zyr.dp.proxy;
 2 
 3 public class Main {
 4 
 5     public static void main(String[] args) {
 6 
 7         Printable proxy=new ProxyPrinter();
 8         proxy.setPrinterName("zyr");
 9         System.out.println("此时代理的名字为:"+proxy.getPrinterName());
10         System.out.println("==遇到了代理处理不了的工作,通知服务器==");
11         proxy.print("hello,world!");
12         System.out.println("====================");
13         proxy.setPrinterName("lsx");
14         System.out.println("此时代理的名字为:"+proxy.getPrinterName());
15         proxy.print("hello,my country!");
16     }
17 
18 }

 
Printable接口:代理的同源性:

```java
 1 package zyr.dp.proxy;
2 
3 public interface Printable {
4 
5     public abstract void setPrinterName(String name);
6     public abstract String getPrinterName();
7     public abstract void print(String word);
8     
9 }

Printer类:本人(相当于真正的服务器)

 1 package zyr.dp.proxy;
 2 
 3 public class Printer implements Printable {
 4 
 5     String name;
 6     public Printer(String name) {
 7         this.name=name;
 8         heavyWork();
 9         System.out.println("生成打印机实例成功...");
10     }
11 
12     //做一点重活
13     private void heavyWork() {
14         System.out.println("本人:"+name);
15         try {
16             Thread.sleep(5000);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20     }
21 
22     public void setPrinterName(String name) {
23         this.name=name;
24     }
25     public String getPrinterName() {
26         return name;
27     }
28 
29     public void print(String word) {
30         System.out.println("打印机"+name+"正在打印...");
31         System.out.println(word);
32         System.out.println("打印完成!");
33     }
34 
35 }

  ProxyPrinter代理类:
   1 package zyr.dp.proxy;
 2 
 3 public class ProxyPrinter implements Printable {
 4 
 5     String name;
 6     Printer printer=null;
 7     
 8     //代理能做的事情自己去做
 9     public synchronized void setPrinterName(String name) {
10         if(printer!=null){
11             printer.setPrinterName(name);
12         }
13         this.name=name;
14     }
15 
16     //代理能做的事情自己去做
17     public String getPrinterName() {
18         return name;
19     }
20 
21     //代理做不了的事情交给真正能做的(打印机)去做
22     public  void print(String word) {
23         check();
24         printer.print(word);
25     }
26 
27     private synchronized void check() {
28         if(printer==null){
29             printer=new Printer(name);
30         }
31     }
32 
33 }

参考:https://www.cnblogs.com/gaochundong/p/least_knowledge_principle.html
https://www.cnblogs.com/gaochundong/p/design_pattern_prototype.html
https://www.cnblogs.com/gaochundong/p/design_patterns.html#design_pattern_space
https://www.cnblogs.com/qjm201000/p/10100601.html
https://www.cnblogs.com/gaochundong/p/design_pattern_decorator.html
https://www.cnblogs.com/gaochundong/p/design_pattern_adapter.html
https://www.cnblogs.com/zyrblog/p/9246438.html
https://www.cnblogs.com/zyrblog/p/9252537.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值