一个菜鸟的设计模式之旅,文章可能会有不对的地方,恳请大佬指出错误。
编程旅途是漫长遥远的,在不同时刻有不同的感悟,本文会一直更新下去。
你想要购买一组运动装备, 比如一双鞋与一件衬衫这样由两种不同产品组合而成的套装。 相信你会想去购买同一品牌的商品, 这样商品之间能够互相搭配起来。如果把这样的行为转换成代码的话, 帮助创建此类产品组的工具就是抽象工厂,便于产品之间能够相互匹配。
案例中 Shoe、Shirt 是两个抽象产品,具体产品NikeShirt、NikeShoe、AdidasShirt、AdidasShoe 是对两个抽象产品的具体分类的实现。ISportsFactory\IAbstractFactory 是抽象工厂接口,里面包含所有产品创建的抽象方法。客户端用的都是抽象产品接口,没有出现任何一个具体产品了字样,达到了解耦的目的!
最大的好处是易于交换产品系列,只需要修改具体的工厂实例!
阿迪达斯工厂正在生产衬衫
阿迪达斯工厂正在生产衬衫
阿迪达斯工厂正在生产鞋子
耐克工厂正在生产衬衫
耐克工厂正在生产衬衫
耐克工厂正在生产鞋子
衬衫 品牌 阿迪达斯 大小 14
衬衫 品牌 阿迪达斯 大小 14
衬衫 品牌 耐克 大小 12
衬衫 品牌 耐克 大小 12
鞋子 品牌 阿迪达斯 大小 10
鞋子 品牌 耐克 大小 12
package main
type IShirt interface {
setLogo(logo string)
setSize(size int)
getLogo() string
getSize() int
}
type Shirt struct {
logo string
size int
}
func (s *Shirt) setLogo(logo string) {
s.logo = logo
}
func (s *Shirt) getLogo() string {
return s.logo
}
func (s *Shirt) setSize(size int) {
s.size = size
}
func (s *Shirt) getSize() int {
return s.size
}
package main
type IShoe interface {
setLogo(logo string)
setSize(size int)
getLogo() string
getSize() int
}
type Shoe struct {
logo string
size int
}
func (s *Shoe) setLogo(logo string) {
s.logo = logo
}
func (s *Shoe) getLogo() string {
return s.logo
}
func (s *Shoe) setSize(size int) {
s.size = size
}
func (s *Shoe) getSize() int {
return s.size
}
package main
type Adidas struct {
}
type AdidasShirt struct {
Shirt
}
type AdidasShoe struct {
Shoe
}
func (a *Adidas) makeShoe() IShoe {
return &AdidasShoe{
Shoe: Shoe{
logo: "adidas",
size: 14,
},
}
}
func (a *Adidas) makeShirt() IShirt {
return &AdidasShirt{
Shirt: Shirt{
logo: "adidas",
size: 14,
},
}
}
package main
type NikeShirt struct {
Shirt
}
type NikeShoe struct {
Shoe
}
type Nike struct {
}
func (n *Nike) makeShoe() IShoe {
return &NikeShoe{
Shoe: Shoe{
logo: "nike",
size: 14,
},
}
}
func (n *Nike) makeShirt() IShirt {
return &NikeShirt{
Shirt: Shirt{
logo: "nike",
size: 14,
},
}
}
package main
import "fmt"
// 这个是抽象工厂接口
type ISportsFactory interface {
makeShoe() IShoe
makeShirt() IShirt
}
// 这里是简单工厂,根据brand生成特定工厂实例
func GetSportsFactory(brand string) (ISportsFactory, error) {
if brand == "adidas" {
return &Adidas{}, nil
}
if brand == "nike" {
return &Nike{}, nil
}
return nil, fmt.Errorf("wrong brand type passed")
}
package main
import "fmt"
func main() {
adidasFactory, _ := GetSportsFactory("adidas")
nikeFactory, _ := GetSportsFactory("nike")
nikeShoe := nikeFactory.makeShoe()
nikeShirt := nikeFactory.makeShirt()
adidasShoe := adidasFactory.makeShoe()
adidasShirt := adidasFactory.makeShirt()
printShoeDetails(nikeShoe)
printShirtDetails(nikeShirt)
printShoeDetails(adidasShoe)
printShirtDetails(adidasShirt)
}
func printShoeDetails(s IShoe) {
fmt.Printf("Logo: %s", s.getLogo())
fmt.Println()
fmt.Printf("Size: %d", s.getSize())
fmt.Println()
}
func printShirtDetails(s IShirt) {
fmt.Printf("Logo: %s", s.getLogo())
fmt.Println()
fmt.Printf("Size: %d", s.getSize())
fmt.Println()
}
Logo: nike
Size: 14
Logo: nike
Size: 14
Logo: adidas
Size: 14
Logo: adidas
Size: 14
namespace 抽象工厂;
public abstract class Shirt
{
public string Logo { get; private set; }
public int Size { get; private set; }
protected Shirt(string logo, int size)
{
Logo = logo;
Size = size;
}
}
public class AdidasShirt : Shirt
{
public AdidasShirt(int size) : base("阿迪达斯", size)
{
}
}
public class NikeShirt : Shirt
{
public NikeShirt(int size) : base("耐克", size)
{
}
}
namespace 抽象工厂;
public abstract class Shoe
{
public string Logo { get; private set; }
public int Size { get; private set; }
protected Shoe(string logo, int size)
{
Logo = logo;
Size = size;
}
}
public class AdidasShoe : Shoe
{
public AdidasShoe(int size) : base("阿迪达斯", size)
{
}
}
public class NikeShoe : Shoe
{
public NikeShoe(int size) : base("耐克", size)
{
}
}
namespace 抽象工厂;
public interface IAbstractFactory
{
Shoe createShoe(int size);
Shirt createShirt(int size);
}
public class AdidasFactory : IAbstractFactory
{
public Shoe createShoe(int size)
{
Console.WriteLine("阿迪达斯工厂正在生产鞋子");
return new AdidasShoe(size);
}
public Shirt createShirt(int size)
{
Console.WriteLine("阿迪达斯工厂正在生产衬衫");
return new AdidasShirt(size);
}
}
public class NikeFactory : IAbstractFactory
{
public Shoe createShoe(int size)
{
Console.WriteLine("耐克工厂正在生产鞋子");
return new NikeShoe(size);
}
public Shirt createShirt(int size)
{
Console.WriteLine("耐克工厂正在生产衬衫");
return new NikeShirt(size);
}
}
// 一个类用于存放工厂单例对象,使用饿汉式单例类
public sealed class AbstractFactorySingleton
{
public static readonly IAbstractFactory Adidas = new AdidasFactory();
public static readonly IAbstractFactory Nike = new NikeFactory();
// 常规写法应该提供一个全局访问点,这里为了方便省略
}
// See https://aka.ms/new-console-template for more information
using 抽象工厂;
// 产品的库存
List<Shoe> shoes = new();
List<Shirt> shirts = new();
IAbstractFactory factory = AbstractFactorySingleton.Adidas;
Shirt s1 = factory.createShirt(14);
Shirt s2 = factory.createShirt(14);
shirts.Add(s1);
shirts.Add(s2);
Shoe s3 = factory.createShoe(10);
shoes.Add(s3);
factory = AbstractFactorySingleton.Nike;
s1 = factory.createShirt(12);
s2 = factory.createShirt(12);
shirts.Add(s1);
shirts.Add(s2);
s3 = factory.createShoe(12);
shoes.Add(s3);
foreach (var shirt in shirts)
{
Console.WriteLine($"衬衫 品牌 {shirt.Logo} 大小 {shirt.Size}");
}
foreach (var shoe in shoes)
{
Console.WriteLine($"鞋子 品牌 {shoe.Logo} 大小 {shoe.Size}");
}
阿迪达斯工厂正在生产衬衫
阿迪达斯工厂正在生产衬衫
阿迪达斯工厂正在生产鞋子
耐克工厂正在生产衬衫
耐克工厂正在生产衬衫
耐克工厂正在生产鞋子
衬衫 品牌 阿迪达斯 大小 14
衬衫 品牌 阿迪达斯 大小 14
衬衫 品牌 耐克 大小 12
衬衫 品牌 耐克 大小 12
鞋子 品牌 阿迪达斯 大小 10
鞋子 品牌 耐克 大小 12
注:把不同的产品变体、产品族理解为不同的产品系列,如现代风格家具类、北约风格家具类。
抽象工厂模式:创建型设计模式。提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:
关键代码:在一个工厂里聚合多个同类产品。
应用场景:
实现步骤:
优点:
缺点:
使用场景:
注意事项:
与其他模式的关系:
在c#的例子中使用了饿汉式单例模式来生成各个工厂实例,这是C#与公告语言运行库提供的一种静态初始化方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下它是不安全的问题。这种静态初始化的方式是在自己被加载时就将自己实例化,所以被称为懒汉式单例类。
更多参考:我的设计模式之旅、02 单例模式 - 小能日记 - 博客园
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
在railstutorial中,作者为什么选择使用这个(代码list10.25):http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-usersnamespace:dbdodesc"Filldatabasewithsampledata"task:populate=>:environmentdoRake::Task['db:reset'].invokeUser.create!(:name=>"ExampleUser",:email=>"example@railstutorial.org",:passwo
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西:
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我