技术标签: Java之设计模式 常用设计模式应用场景分析 Java常用设计模式
掌握常用的几种(最起码单例模式、工厂模式),了解其他的设计模式即可,做到手里有粮,心里不慌。首先,掌握每种模式的定义及使用场景。其次,掌握一个形象的例子,简单的过一遍代码。
学习设计模式的真正目的:编程时,有意识地面向接口编程,多用封装、继承、组合、多态等OOP思想,而不仅仅是死记几类设计模式。
常用的设计模式分类:
创建型(创建一个对象):单例模式、工厂模式、抽象工厂模式
结构型(将几个对象组织成一个结构):桥接模式、外观模式、代理模式
行为型(多个对象间的通信):观察者模式、策略模式
其中,工厂模式、桥接模式、策略模式有点像,放在一起理解(几个对象具有共同特征,因此继承共同的接口,然后通过工厂、桥去访问)。另外,工厂模式和外观模式(几个对象间有先后关系,是串行的,而非工厂模式中的并行,因此几个对象组合成一个外观类,通过这个外观类来访问)区别很明显,也因此放在一起理解。
参考引用:
http://www.runoob.com/design-pattern/proxy-pattern.html
https://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html
https://www.cnblogs.com/chinajava/p/5880870.html
被反复使用的,代码设计经验的总结。
总结起来,就是多用接口/抽象类,从而增加代码的可扩展性(减少修改代码)。降低模块间的依赖和联系。
体现了OOP的模块化、可扩展性等特征。
定义与使用场合:现在需要创建几个对象,且这几个对象有共同特征,则不需要具体创建各个对象,而是创建对象工厂类即可。
一般常用静态工厂模式。
例子:发送邮件和短信(共同特征:发送的消息)
public interface Sender {
public void Send();
}
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
public class SendFactory {
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.Send();
}
}
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。
定义与使用场景:同上。
例子:同上。
public interface Provider {
public Sender produce();
}
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
public class Test {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
总结:如果要新增发送微信,则只需做一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口,就OK了,无需去改动现成的代码。这样做,拓展性较好!
所有工厂模式中,抽象工厂模式最先进。
定义与使用场合:一个系统需要动态地在几种类似的算法中选择一种。
与工厂模式异同:实例化一个对象的位置不同。对工厂模式而言,实例化对象是放在了工厂类里面。而策略模式实例化对象的操作在调用的地方。本质都是继承与多态。
例子: 现有 加/减/乘 几种算法,输入参数返回值都一样(可以理解成类似的算法)。现在需要在调用时动态配置算法策略,实现对不同算法的调用。
public interface Strategy {
public int doOperation(int num1, int num2);
}
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
public class OperationSubstract implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
public class OperationMultiply implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
}
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
public class StrategyPatternDemo {
public static void main(String[] args) {
//实例化对象的位置在调用处
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
定义及使用场合:只有一个对象被创建。
例子:
建议采用 饿汉式 创建方法。线程安全,容易实现。初始化慢一点。
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
}
定义与使用场景:一个对象(subject)被其他多个对象(observer)所依赖。则当一个对象变化时,发出通知,其它依赖该对象的对象都会收到通知,并且随着变化。
比如 声音报警器和闪光灯报警器分别订阅热水器温度,热水器温度过高时,发出通知,两个报警器分别发声、闪光以实现报警。
又比如很多人订阅微信公众号,该公众号有更新文章时,自动通知每个订阅的用户。
**实现:**1,多个观察者要订阅这个对象 2,这个对象要发出通知
例子:
public interface Observer {
public void update();
}
public class Observer1 implements Observer {
@Override
public void update() {
System.out.println("observer1 has received!");
}
}
public class Observer2 implements Observer {
@Override
public void update() {
System.out.println("observer2 has received!");
}
}
public interface Subject {
/*增加观察者*/
public void add(Observer observer);
/*删除观察者*/
public void del(Observer observer);
/*通知所有的观察者*/
public void notifyObservers();
/*自身的操作*/
public void operation();
}
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vector = new Vector<Observer>();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> enumo = vector.elements();
while(enumo.hasMoreElements()){
enumo.nextElement().update();
}
}
public class MySubject extends AbstractSubject {
@Override
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}
public class ObserverTest {
public static void main(String[] args) {
Subject sub = new MySubject();
sub.add(new Observer1()); //订阅这个对象
sub.add(new Observer2());
sub.operation(); //发出改变的一个通知
}
}
定义与使用场景:一个代理类代表一个真实类的功能,通过访问代理类来实现对真实类的访问。
比如买火车票这件小事:黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。
又比如需要对原有的方法进行修改,就是采用一个代理类调用原有的方法,以避免修改原有代码。
例子:
一个真实对象realSubject提供一个代理对象proxy。通过proxy可以调用realSubject的部分功能*,并添加一些额外的业务处理*,同时可以屏蔽realSubject中未开放的接口。
1、RealSubject 是委托类,Proxy 是代理类;
2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;
interface Subject {
void request();
}
class RealSubject implements Subject {
public void request(){
System.out.println("RealSubject");
}
}
class Proxy implements Subject {
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("begin");
subject.request();
System.out.println("end");
}
}
public class ProxyTest {
public static void main(String args[]) {
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
定义与使用场景:访问多种数据库驱动(多个具有共同特征的数据库驱动),不是直接访问,而是通过DriverManager桥来访问。
例子: 不再具体实现了。
与策略模式的区别:(个人觉得较复杂,了解即可。本质都是面向接口编程,体现继承与多态)
策略模式:我要画圆,要实心圆,我可以用solidPen来配置,画虚线圆可以用dashedPen来配置。这是strategy模式。
桥接模式:同样是画圆,我是在windows下来画实心圆,就用windowPen+solidPen来配置,在unix下画实心圆就用unixPen+solidPen来配置。如果要再windows下画虚线圆,就用windowsPen+dashedPen来配置,要在unix下画虚线圆,就用unixPen+dashedPen来配置。
所以相对策略模式,桥接模式要表达的内容要更多,结构也更加复杂。
定义与使用场景:见例子。又比如,去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便。
例子:计算机启动,需要先启动CPU,再启动memory,最后启动disk。这三个类之间具有先后关系(依赖关系)。
与工厂模式的区别:工程模式多个类具有共同特征(继承一个共同的接口),是并列的。而外观模式多个类是有先后关系,是串行的,用组合。
贴部分代码:
public class Computer {
//是组合,而非继承。这是与工程模式的显著区别。
private CPU cpu;
private Memory memory;
private Disk disk;
public Computer(){
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup(){
System.out.println("start the computer!");
cpu.startup();
memory.startup();
disk.startup();
System.out.println("start computer finished!");
}
public void shutdown(){
System.out.println("begin to close the computer!");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("computer closed!");
}
}
public class User {
public static void main(String[] args) {
Computer computer = new Computer();
//将计算机的启动过程封装成一个类
computer.startup();
computer.shutdown();
}
}
定义与使用场景:生产者把数据放入缓冲区,而消费者从缓冲区取出数据。
例子:缓冲区一般为队列(FIFO),但在生产消费较为频繁时,队列push,pop内存消耗较大,此时可以考虑环形缓冲区(以数组、链表方式实现)。
通过互斥锁防止缓冲区同时读写。通过信号量控制缓冲区大小(满的时候不允许写,空的时候不允许读)
作者:qq_14827935
来源:CSDN
原文:https://blog.csdn.net/qq_14827935/article/details/78618652
文章浏览阅读1k次。图书馆图书借阅管理系统能做到的不仅是大大简化管理员的信息管理工作,在提高图书馆管理效率的同时还能缩减开支,更能在数字化的平面网络上将图书馆管理最好的一面展示给客户和潜在客户,而这个系统在带给图书馆管理全新用户信息统计和分类的同时,还成为日后图书馆管理制定管理方式的重要数据参考。过程永远比结果重要。毕业设计是大学生活中最为浓墨重彩的一笔,在这个过程中不仅学到更为全面的书本和实践知识,更让我感受到了浓浓的同窗之情及师生情本python+django+vue+Elementui+mysql系统可以定制,采用py_基于python图书管理系统的ppt
文章浏览阅读1.7k次,点赞5次,收藏10次。init进程是Android系统在内核启动完毕之后,启动的第一个进程。这个进程会创建运行Android上传所需要的各种运行环境。这篇博文主要分析 init进程具体是如何进行 init.rc 以及其他的rc文件的。..._output/vendor/etc/init/qvrd vndr.rc: 23: nvalid keyword 'writepid
文章浏览阅读1.6k次。文章目录千万级的数据库查询中,如何提高效率?数据库设计方面SQL语句方面Java方面千万级的数据库查询中,如何提高效率?数据库设计方面1.对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引。2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num is null可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select i_千万级的数据sql怎样快速查询
文章浏览阅读6.2k次。具体原因:电脑的计算机名称因特殊原因进行更改,更改后wincc项目便无法再次打开,每次打开wincc项目,项目一直显示正在打开,具体修复措施如下:一:先将计算机名称改回到原来名称。打开控制面板,显示小图标,找到管理工具,再找到事件查看器。在事件查看器可找到修改信息。二:打开计算机,找到Rest_Wincc文件。具体文件地址如下:C:\Program Files (x86)\SIEMEN..._该计算机上没有激活的wincc项目
文章浏览阅读3.7k次,点赞3次,收藏4次。Base64 是一种常用的编码方式,用于将二进制数据转换为可打印的 ASCII 字符串。它的主要特点如下:1. 字符集:Base64 使用了 64 个字符来表示数据,包括大小写字母(A-Z, a-z)、数字(0-9)以及两个特殊字符(+ 和 /)。2. 填充字符:Base64 编码后的字符串长度通常不会与原始数据长度完全对齐。为了保持长度整齐,Base64 在末尾添加一个或两个 `=` 号作为填充字符。3. 编码方式:Base64 编码将每 3 个字节作为一组,转换为 4 个 Base64 字符。_base64解码
文章浏览阅读1.9k次。Web开发:设置复选框的只读效果在Web开发中,有时候需要显示一些复选框(checkbox),表明这个地方是可以进行勾选操作的,但是有时候是只想告知用户"这个地方是可以进行勾选操作的"而不想让用户在此处勾选(比如在信息展示页面),这时候就需要将复选框设置成只读的效果。提到只读,很容易想到使用readonly属性,但是对于复选框来说,这个属性和期望得到的效果是有差别的。原因在于readonly属性关..._为什么在hbuilder中两个复选框勾选不了
文章浏览阅读824次。<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>插入内容为HTML代码</title> <script src="../js/vue.js"></script></head><body> <div id="app"> <h2&g._js 操作dom 插入html
文章浏览阅读6.6k次,点赞37次,收藏24次。选择合适的软路由应基于实际需求和使用场景,对于普通用户和专业用户来说,不同的软路由可能有不同的优势和适用性。最终选择应当考虑网络功能、易用性、稳定性以及所需的特定功能需求。_routeros和爱快哪个强大
文章浏览阅读6.2w次,点赞8次,收藏20次。iText5.x版本以上中的font和encoding文件都是从String RESOURCE_PATH = “com/itextpdf/text/pdf/fonts/”加载的,而itextasian1.5.x.jar的包名是com.lowagie.text.pdf.fonts, 包名不一致,导致路径错误。解决方法如下: 1.将itextasian1.5.x.ja解压,找到里面itextasi......_font 'stsong-light' with 'unigb-ucs2-h' is not recognized.
文章浏览阅读3.7k次。目前有以下几种网页游戏服务器:利用.asc通信文件访问其他高级语言,比如java,vc,.net等访问数据库1)FMS(Flash Media Server)用AS2.0或者AS1.0来构建服务器端的,而客户端可以用AS3.0。在视频方面比较有优势,但是一般现在要结合其他语言开发比如NET类的。flash聊天室啊,在线视频会议啊啊, 网络_flash项目技术方案
文章浏览阅读1.6k次。人工智能(Artificial Intelligence):人工智能是一个广泛的概念,指的是使计算机系统具备像人类一样的智能和能力。人工智能涵盖了包括机器学习和深度学习在内的各种方法和技术,旨在让计算机能够感知、理解、推理、学习和解决问题。人工智能的目标是模拟和实现人类智能的各个方面,以改善生活、提高效率和解决复杂的问题。机器学习(Machine Learning):机器学习是一种人工智能的方法和技术,旨在使计算机系统能够从数据中学习和改进,而无需明确编程。_ai 机器学习 深度学习关系
文章浏览阅读3.2k次,点赞4次,收藏13次。1. Secure boot概述本文档主要是secure boot方案的介绍和说明,其内容会涵盖以下方面:secure boot的目的和介绍、技术方案的描述、PC端签名工具和Image download&update工具的使用以及产线实施所需要做的准备工作和注意事项等。1.1. 需求与目的目前,非授权更改甚至替换手机原版操作系统中固有软件或者操作系统的软件技术手段层出不穷,se..._spreadtrumtools