老僧长谈设计模式-2-工厂模式

声明:本节内容主要来自Head First 一书

【概述】

工厂方法模式(Factory Method  Pattern)通过让子类决定创建的对象是什么,来达到将对象创建的过程封装的目的。

【目的】

使用工厂模式,会给你系统带来更大的可扩展性和尽量少的修改量。

符合 开-闭 设计原则。

【讲故事】

复杂的逻辑要用简单的故事讲清楚,是一个聪明的选择。

假设你有一个匹萨店

public  class PizzaStore
    {
       public Pizza orderPizza(string type) 
       {
           Pizza pizza;

           //根据Pizza类型,我们实例化正确的具体类。
           //这里的任何Pizza都必须实现Pizza接口
           if (type.Equals("cheese")) 
           {
               pizza = new CheesePizza();
           }else if (type .Equals ("greek"))
           {
               pizza = new GreekPizza();
           }else if (type .Equals ("pepperoni"))
           {
               pizza = new PepperoniPizza();
           }

           //pizza 的一些制作流程
           pizza.prepare();
           pizza.bake();
           pizza.cut();
           pizza.box();
           return pizza;

       }
    }

但是你需要更多匹萨类型。。。。。

这样就存在一个问题:如果市场需求变化(出现新的风味的Pizza需求),那我们就要对这段代码一改再改。
违反了我们的设计原则:类应该对扩展开发,对修改关闭。
那怎么办?


封装创建对象的代码

把创建匹萨打代码移到另一个对象中由新对象专职创建匹萨,称这个新对象为 工厂。


建立一个简单的匹萨工厂

public  class SimplePizzaFactory
    {
       public Pizza createPizza(String type) 
       {
           Pizza pizza = null;
           
           if (type.Equals("cheese"))
           {
               pizza = new CheesePizza();
           }
           else if (type.Equals("greek"))
           {
               pizza = new GreekPizza();
           }
           else if (type.Equals("pepperoni"))
           {
               pizza = new PepperoniPizza();
           }
           return pizza;
       }
    }

问:这么做有什么好处?似乎只是把问题搬到另一个对象罢了,问题依然存在。

答:别忘了,SimplePizzaFactory可以有许多的客户,虽然目前只看到orderPizza()方法是它的客户,
然而,可能还有PizzaShopMenu(比萨店菜单)类,会利用这个工厂来取得比萨的价钱和描述。

可能还有一个HomeDelivery(宅急送)类,会以与PizzaShop类不同的方法来处理匹萨。
所以,把创建比萨的代码包装进一个类,当以后实现改变时,只需要修改这个类即可。


经过不断的改造,你的匹萨店经营有成,大家都想做你的加盟店。那么问题来了,每家加盟店都可能想要提供不同风味的匹萨(纽约、芝加哥、加州),

你希望加盟店都能利用你的代码,好让匹萨的流程能保持不变。

你想让加盟店采用你的工程创建匹萨,在烘烤和切片的部分流程也要和你保持一致,那么你需要一个框架把加盟店和创建匹萨捆绑在一起的同时又保持一定的弹性。

创建者类

public abstract class PizzaStore{
	public Pizza orderPizza(String type){
		Pizza pizza;
		pizza = createPizza(type);
		
		pizza.prepare();
		pizza.bake();
		pizza.cute();
		pizza.box();
		
		return pizza;
	}
	
	//工厂方法
	protected abstract Pizza createPizza(String type);
	
	//其他的方法
	....
}

这样每个加盟店就可以各自决定如何制造匹萨啦,这样就保持了弹性。

是时候在纽约开一家匹萨店了:

public class NYPizzaStore extends PizzaStore {

    /**
     * 
     * @param item
     * @return 返回一个Pizza对象
     * 由子类全权负责该实例化哪一个具体Pizza
     */
	Pizza createPizza(String item) {
        if (item.equals("cheese")) {
            return new NYStyleCheesePizza();
        } else if (item.equals("veggie")) {
            return new NYStyleVeggiePizza();
        } else if (item.equals("clam")) {
            return new NYStyleClamPizza();
        } else if (item.equals("pepperoni")) {
            return new NYStylePepperoniPizza();
        } else return null;
    }
}


刚刚忽略了一个类,匹萨类本身

public abstract class Pizza {  
    protected String name;        //名称  
    protected String dough;       //面团  
    protected String sause;       //酱料  
    protected List<String> toppings = new ArrayList<String>();       //佐料  
      
      
    public void prepare() {  
        System.out.println("Preparing "+name);  
        System.out.println("Tossing dough");  
        System.out.println("Adding sause");  
        System.out.println("Adding toppings");  
        for(int i = 0;i < toppings.size();i++){  
            System.out.println("   "+toppings.get(i));  
        }  
    }  
  
    public void bake() {  
        System.out.println("Bake for 25 minutes at 350");  
    }  
  
    public void cut() {  
        System.out.println("Cutting the pizza into diagonal slices");  
    }  
  
    public void box() {  
        System.out.println("Place pizza in official PizzaStore box");  
    }  
      
    public String getName(){  
        return name;  
    }  
}  


纽约匹萨

public class NYStyleCheesePizza extends Pizza{  
    public NYStyleCheesePizza(){  
        name = "Ny Style Sauce and Cheese Pizza";  
        dough = "Thin Crust Dough";  
        sause = "Marinara Sauce";  
          
        toppings.add("Crated Reggiano Cheese");  
    }  
  
}

芝加哥匹萨

public class ChicagoStyleCheesePizza extends Pizza {  
    public ChicagoStyleCheesePizza(){  
        name = "Chicago Style Deep Dish Cheese Pizza";  
        dough = "Extra Thick Crust Dough";  
        sause = "Plum Tomato Sauce";  
          
        toppings.add("Shredded Mozzarella Cheese");  
    }  
      
    public void cut(){  
        System.out.println("Cutting the Pizza into square slices");  
    }  
}  


你已等的太久,匹萨开卖了

public class PizzaTestDrive {  
    public static void main(String[] args) {  
        System.out.println("---------Joel 需要的芝加哥的深盘披萨---------");  
        ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore();       //建立芝加哥的披萨店  
        Pizza joelPizza =chicagoPizzaStore.orderPizza("cheese");             //下订单  
        System.out.println("Joel ordered a " + joelPizza.getName() + "\n");  
          
        System.out.println("---------Ethan 需要的纽约风味的披萨---------");  
        NYPizzaStore nyPizzaStore = new NYPizzaStore();  
        Pizza ethanPizza = nyPizzaStore.orderPizza("cheese");  
        System.out.println("Ethan ordered a " + ethanPizza.getName() + "\n");  
          
    }  
}

认识工厂模式的时刻终于到了

【类图】

简单工厂模式:


工厂模式:




匹萨店图解析

纽约匹萨:薄饼,美味的酱料和少量的芝士

芝加哥匹萨:厚饼,重味的酱料和大量的芝士




工厂模式的精髓:

工厂模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。

工厂方法的优势:

1 解决了对修改关闭,对扩展开放的问题。

2 将创建对象的代码集中在一个对象或方法中,可以避免代码的重复,方便以后的维护

3 客户在实例化对象时,只会依赖于接口,而不是具体类。针对接口编程,而不是针对实现编程。


编程建议:

  • 不要让类派生自具体类

结束语:

谢谢观看。


在使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值