简单地设计模式
设计模式包括很多原则,但是学习他我们要先从类图等uml的东西去了解他。
那就先开始学习uml里面的类图。
一 类图:
1 类之间的几种关系:1 泛化
2 实现
3 关联:一般联合,聚合,组合。
4 依赖
2 类图中的类:包括三个东西,分别是名称(name).属性().操作(function)。
3 接口:两行。
4 类之间的几种关系:就是第一点要指出的。
(1) 泛化:指继承。用子类指向父类,实线,空三角。 父类 <← 子类,和这个比较类似,但是图库资源有限,只好先这样
(2) 实现:指实现接口。用实现类指向接口,虚线,空三角。
(3) 一般关联:比如老师和学生。没有什么大的关联,但是有一些。用一条没有箭头的实线就好。
(4) 聚合:整体与部分,且整体与部分分离,部分能单独存在。比如汽车与轮胎。用部分指向整体,且为实线,空菱形。
(5) 组合:整体与部分,且整体与部分分离,部分不能单独存在。比如公司与部门,部门与小组。用部分指向整体,且为实线,实心菱形。
(6) 依赖:其实任何事物都有依赖,所以一般也不会用到这层关系。
这些关系可以做大小比较:泛化=实现>组合>聚合>关联>依赖。
工厂模式
这次要学习工厂模式,工厂模式是用来new 一个对象,一般new对象都要在main这种主方法里面,
如果要是new 5到6个对象,那么一个main就要依赖5到6个类。并且还需要知道类名,导致消息的泄露。因此就有了工厂模式,工厂模式是生产对象的工厂
工厂模式包括三种:简单工厂模式,工厂模式,抽象工厂模式。
简单工厂模式:一个工厂类,里面有一个静态方法,可以创建对象,其中根据传入的参数不同就可以new不同的对象,如果可以的话,可以直接通过先解析xml或者properties,然后利用反射(class.forname())去创建类,然后再getinstance,这样就可以创建对象。如果改变对象,只需要改变对象的xml或是properties。
重:其中用到一个知识点:父类对象可以被子类对象替代。比如:class A是class B的父类,则可以
A a=new B();这样是可以的。
工厂模式:为了解决简单工厂模式的不符合开毕原则(开毕原则:即可以添加代码,尽量不去修改代码),由于要想新建对象,简单工厂模式实际上是需要修改以前的工厂代码的(比如要添加一个类),所以为此我们需要创建一个工厂接口,一种商品就继承一个接口,比如要一个电视接口,那么以前是就一个工厂,判断需要哪个对象就new 哪个对象,如果是工厂模式,则海尔电视一个工厂,海信电视一个工厂,这样,如果再增加一个tcl电视,那么继承电视接口添加代码就好,如果是以前,则就会需要改变工厂类的代码,去再添加一个tcl电视的判断。
接着就是抽象工厂模式
首先:简单工厂模式:可以生产任何产品。
工厂模式:只可以生产一种产品。
抽象工厂模式:一个工厂可以生产多个商品:比如电脑需要生产cpu 键盘 鼠标 显示器 显卡等。
抽象工厂模式首先也需要一个工厂接口,然后可以两个厂子去继承他(比如海尔与海信),然后再里面鼓捣,这和工厂模式是一样的,区别就是工厂模式只new一个东西,比如电脑(new computer()),而这里面需要new 好几个,比如(new cpu(),new 键盘(),new 显卡())。区别就在这里。实际上是差不多的。
下面讲单例模式:单例模式的目的是就是让整个程序里只有一个实例,比如我们所熟知的windows操作系统,如果你打开记事本这个软件,你可以打开n次,如果你打开资源管理器,你会发现无论你打开多少次,都会只产生一个资源管理器。
单例模式写的时候一定注意1个地方:1 构造器一定要private(这样就不能new 这个class).
单例模式包括两种情况:1 饿汉模式 2 懒汉模式
1 饿汉模式:顾名思义,特别饿,所以只要一加载类(即把类放任jvm(把.java转化为.class)),就要生成这个属性(而且为了可以不被再建立属性,一定要添加static),但是这样就会时间换空间(static占据空间,但是一加载类就生成(节省时间))
2 懒汉模式:就是特别懒,需要的时候才需要去new 所以要先判断有没有这个实例,然后才new,注意这里一定要注意线程安全,因为如果但时有两个线程去请求这个,一个线程判断空,此时切换到另一个线程,另一个线程判断不为空,此时又切换到原来那个线程,原来那个线程就是认为他会不为空,结果就不去创建线程,导致错误,此时就会需要synchronized,这样就会保护代码段。
3 序列化和反射会破解这个单例模式。反破解。
懒汉式:
Class b{
Private static b ins;
Private b(){}
Public static synchronized getins(){
If(ins!=null){
Ins=New b();
Return ins;
}
}
}
饿汉式:
Class a{
private static a instance=new a();
Private a(){}
Public static getins(){
Return instance;
}
}
建造者模式
这堂课我们来学习建造者模式:
这个模式是非常重要,但是却有点复杂。他也是创建新的对象的模式。
需要四个原料:1 产品(product) 2 建造者接口(abstract builder) 3 建造者类(builder) 4 服务员类(director)
产品:比如生产一个汽车。(他包括车轮 座椅 发动机)。
建造者接口:这里必须要有建造这个的方法:craetetyre(); createengine(); createseat(); 。
建造者类:实现这些方法。
服务员类(这个名字自己起的,有可能不到位):在这里面把建造车的方法都封装在一起,这样client里面就可以直接调用这个方法而不是一个一个使用建造的方法。
上代码:现在就把所有的放到一起,但是会拿--------------------(线)去分隔
原型模式
这次我们学习原型模式,他仍然是一个创建型模式。用来创建对象,其中分为深克隆还有浅克隆,二者的区别是浅克隆只克隆名字(即对象),而深克隆克隆所有,这个需要自己体会。
下来我们先来学习浅克隆
浅克隆:
重中之重:三个条件:
1 实现cloneable接口
2 重写clone方法。(必须有super.clone())
3 return clone。
剩下的只是需要new 就好
代码:
服务器:package clonedesign;
public class cloneexamp implements Cloneable {
//需要一个变量,并定义
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//这时cloneable只是一个标示,和serelizable是一样的
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
这是客户端:
public class client {
public static void main(String[] args) {
cloneexamp clone1=new cloneexamp();
clone1.setName("lc");
cloneexamp clone2=null;
try {
clone2 = (cloneexamp) clone1.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(clone1==clone2);
System.out.println(clone2.getName());
}
}
输出:false lc
注:我们克隆啥,啥的地址就会成为新的,意思是就像上一个例子,克隆对象,则对象不一样,输出false,而指向的空间不变,因此都指向name为lc的空间,而如果此时把name这个属性也克隆,那么就不会输出lc。因为name有了新地址。
深克隆:
一定要完成三点:
1 实现serializable接口。
2 创建deepclose()方法(自己创建的,你应该知道serializable不需要实现任何方法)。
3 在deepclone()里面一定要序列化,反序列化(即inputstream outputstream)
我本人不觉得深克隆比较好。
深克隆是复制所有,即所有东西克隆后都不一样,所有都有了新的东西。
所以我比较喜欢浅克隆。
重重重重重重:那么克隆是干啥的,为啥要用它
克隆一点:不管深克隆还是浅克隆,创建的速度都要比new强大很多倍。如果这个class很简单,那么或许new快一些,如果class复杂,并且需要五十万个对象,你可以试试,克隆会比new快不知道多少倍
浅克隆又一点:电脑上的复制粘贴用的就是浅克隆,一个word文档复制之后,那么对象地址变了,指向的内容一样,这样很节省空间。
适配器模式
这次我们学习适配器模式:
适配器模式就是adapter1,最简单的就是比如:德国的插座只能插两个,并且两个比较厚圆,而中国的可以插三个,且是扁头,
这个时候就需要适配器。
重中之重:适配器模式是啥:就是比如有两个东西A,B,这个随便,适配器C必须把二者关联起来,不管二者是啥。为此有两种方法:一种是对象适配器模式,另一个是类适配器模式。
所以其实适配器就需要三个A B C.
使用的情况是啥?人们说让B继承A,这样不符合情况,比如已经写好了机器人类和狗类,并且想让二者有关联,这时如果让狗继承机器人,显然要修改代码,违反开闭原则,况且狗已经继承了动物类呢,单继承是不能再继承的。
上代码:
类适配器模式:代码附adapter文件夹。
桥接模式:
1 这次讲的是桥接模式。
桥接模式,是在已经有很多东西的时候添加的。
比如:生产电脑,电脑是一个接口,而电脑的型号是平板,笔记本,台式,所以这三个要实现电脑接口,但是,不光如此,电脑还是有品牌的,即苹果的电脑(台式,平板,笔记本),还有可能是索尼的(平板电脑,台式电脑,笔记本电脑)。当然还有可能是小米的。
那么按照普通的模式来说,则是这么写:
interface computer{
void sale();
}
//台式
class pc implements computer{
void sale(){
System.out.println("this is pc");
}
}
//笔记本
class laptop implements computer{
void sale(){
system.out.println("this is laptop");
}
}
//平板
class pad implements computer{
void sale(){
System.out.println("this is pad");
}
}
class hasee extends pc{
void sale(){
System.out.println("this is hasee pc");
}
}
class hasee extends Laptop{
void sale(){
System.out.println("this is hasee laptop");
}
}
class hasee extends pad{
void sale(){
System.out.println("this is hasee pad");
}
}
class sony extends pc{
void sale(){
System.out.println("this is sony pc");
}
}
class apple extends pc{
void sale(){
System.out.println("this is apple pc");
}
}
这么写的后果是什么,比如现在已经有了苹果电脑,我需要在增加一个品牌,比如联想,那么需要写三个类,即(class lenovopc extends pc,class lenovopad extends pad ,class lenovo extends laptop);如果是不这么写的话,那么是这样,需要一个品牌的接口,以下是代码:
package designpatttern;
public abstract class computer {
//abstract 不只有方法(没有实现的),也可以有实现的方法,也可以属性。
private Brand brand;
public computer(Brand b){
this.brand=b;
this.brand.isbrand();
}
abstract void sale();
}
class pc extends computer{
public pc(Brand b) {
super(b);
// TODO Auto-generated constructor stub
}
@Override
void sale() {
// TODO Auto-generated method stub
System.out.println("这是台式电脑");
}
}
class pad extends computer{
public pad(Brand b) {
super(b);
// TODO Auto-generated constructor stub
}
@Override
void sale() {
// TODO Auto-generated method stub
System.out.println("这是平板");
}
}
class laptop extends computer{
public laptop(Brand b) {
super(b);
// TODO Auto-generated constructor stub
}
@Override
void sale() {
// TODO Auto-generated method stub
System.out.println("这是笔记本");
}
}
-------------------------------------------------------------------------------------------------------------------------这是品牌
package designpatttern;
public interface Brand {
void isbrand();
}
class lenovo implements Brand{
@Override
public void isbrand() {
// TODO Auto-generated method stub
System.out.println("这是联想");
}
}
class apple implements Brand{
@Override
public void isbrand() {
// TODO Auto-generated method stub
System.out.println("这是苹果");
}
}
class sony implements Brand{
@Override
public void isbrand() {
// TODO Auto-generated method stub
System.out.println("这是索尼");
}
}
这种情况的好处显而易见:即比如需要一个新的品牌(比如华硕),则只需要写一段代码,即一个class(class 华硕 implements Brand)
极大的节省了代码。
装饰者模式
这次学习装饰者模式:
主要用处就是:
1 避免了不能多继承所带来的不变。即如果一个车能在地面上跑,子类有飞的功能,子类有在水里开的功能,子类还有自动驾驶的功能,这三个功能是三个类,这三个类分别继承地面上跑的功能,如果还有一个车既要飞,也要在水里就不好了,因为,不能继承两个父类,
聪明的你应该想到了对策。
2 对策就是继承,只不过,B继承A,C继承B,这三者通过super关键字在叠加操作,
代码:
interface Icar{
public void move();
}
final class car implements Icar{
public void move(){
SYstem.out.println("能在地上跑");
}
}
class supercar implements Icar{
protected Icar car;
public supercar(Icar car){
super();
this.car=car;
}
public void move(){
car.move();
}
}
class flycar extends supercar{
public flycar(Icar car){
super(car);
}
public void fly(){
System.out.println("可以飞的车");
}
public void move(){
super.move();
this.fly();
}
}
class swimcar extends supercar{
public swimcar(Icar car){
super(car);
}
public void swim(){
System.out.println("可以游泳的车");
}
public void move(){
super.move();//上一个的所有再加上这一个的新方法
this.swim();
}
}
class AIcar extends supercar{
public swimcar(Icar car){
super(car);
}
public void AI(){
System.out.println("可以自动驾驶的车");
}
public void move(){
super.move();
this.AI();
}
}
class client{
public static void main(String []args){
System.out.println("-------------------");
Icar car=new Car();
car.move();
System.out.println("-------------------");
car=new flycar(car);
car.move();
System.out.println("-------------------");
car=new swim(car);
car.move();
System.out.println("-------------------");
car=new AIcar(car);
car.move();
}
}
最经典的用法就是java的io流,
BUfferedReader bur=new BufferedReader(new InputStreamReader(new FileInputStream(“./a.txt”)));
重重重重:super.move(),这里指的是应该是父类的move()也就是supercar的move()方法,里面实现了car.move();这个car.move()中的car是所传递过来的car对象(这个对象就是this.car=car,因此就是传递过来的对象的move()),这样新的move()就包括了两个,即super.move()和新的方法,比如this.swim(),这样吧他们打包生成新的move(),这样又会被叠加,也就造成了叠加。所谓的装饰器就是把move这个方法和其传过来的对象对应上。然后每次都有新的方法从而导致了叠加。
叠加要满足两点:1 装饰器的方法里面的构造器的对象要和其方法对应上。
2 方法的叠加move()里面要嵌套一个super.move()
多写写就会了。
外观模式:
外观模式很好理解,其实就是一层封装,
比如:你要喝茶,需要先准备茶杯,接着准备茶叶,然后烧水,再就是准备泡茶,那么现在有好几个人,张三,李四,王五,他们每个人喝茶都要完成这么多类,完成这么多方法,那么此时就需要一个服务员,如果服务员把所有的事都做好,那么我们需要做的事就是把服务员叫过来,然后让他做好茶,我们直接喝,其实就是一个封装的问题。
享元模式
这次我们学习享元模式。
解决问题:比如说下围棋,我们一般围棋有很多棋子,比如黑色棋子100个,白色棋子100个,首先声明黑色棋子,因为每个黑棋子的位置不同(虽然颜色相同),所以按照以前的做法肯定是声明100个对象,那么这样会大大的浪费内存资源,但是我们想一想,其实他们的颜色相同,只是位置不同,那么其实如果只是声明一个对象就可以。
思路:就是分为两个类,一个类做公有的东西(颜色),另一个就是做私有的东西(位置)
代码如下:
package chessflyweight;
import java.util.HashMap;
import java.util.Map;
public interface chessflyweight {
void display(coordinate c);
}
class concreteChess implements chessflyweight{
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public void display(coordinate c) {
// TODO Auto-generated method stub
System.out.println("位置为:x-"+c.getX()+"y-"+c.getY());
System.out.println("颜色为:"+this.color);
}
}
class coordinate{
private int x;
private int y;
public coordinate(int x,int y){
this.x=x;
this.y=y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
class chessFlyWeightFactory{
private static String color;
public chessFlyWeightFactory(String color){
this.color=color;
}
private static Map<String,chessflyweight> map=new HashMap<String,chessflyweight>();
//静态变量的获得需要静态方法
public static chessflyweight getchess(){
if(map.get(color) != null){
return map.get(color);
}else{
//就要声明这个对象了
chessflyweight cfw=new concreteChess();
map.put(color, cfw);
return cfw;
}
}
}
其实就是把对象放到一个map里面,如果存在这个对象,则直接get这个对象,没有则创建这个对象,并push到map里面,并且由于每个棋子的位置不同,则每次创建的位置可以不同。但是对象一样。即棋子只需创建一个,我们只是需要报告他的位置,即其中有一个参数不同,但是他的对象都一样。

浙公网安备 33010602011771号