{"id":16246287,"url":"https://github.com/dingpingzhang/simplefactorygenerator","last_synced_at":"2025-03-19T19:30:39.329Z","repository":{"id":128098653,"uuid":"474700897","full_name":"DingpingZhang/SimpleFactoryGenerator","owner":"DingpingZhang","description":"A simple factory source generator that enables the pattern to not violate the open-close principle.","archived":false,"fork":false,"pushed_at":"2023-10-24T14:27:24.000Z","size":151,"stargazers_count":51,"open_issues_count":0,"forks_count":7,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-17T10:03:03.136Z","etag":null,"topics":["architecture","design-pattern","design-patterns","design-principles","factory-method","factory-method-pattern","simple-factory","simple-factory-pattern"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DingpingZhang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-27T16:53:46.000Z","updated_at":"2025-03-10T07:03:08.000Z","dependencies_parsed_at":null,"dependency_job_id":"2abecad4-eeaa-4baf-a6b6-d5fc7f017061","html_url":"https://github.com/DingpingZhang/SimpleFactoryGenerator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DingpingZhang%2FSimpleFactoryGenerator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DingpingZhang%2FSimpleFactoryGenerator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DingpingZhang%2FSimpleFactoryGenerator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DingpingZhang%2FSimpleFactoryGenerator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DingpingZhang","download_url":"https://codeload.github.com/DingpingZhang/SimpleFactoryGenerator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244492708,"owners_count":20461553,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["architecture","design-pattern","design-patterns","design-principles","factory-method","factory-method-pattern","simple-factory","simple-factory-pattern"],"created_at":"2024-10-10T14:30:12.068Z","updated_at":"2025-03-19T19:30:39.042Z","avatar_url":"https://github.com/DingpingZhang.png","language":"C#","readme":"﻿# SimpleFactoryGenerator [![version](https://img.shields.io/nuget/v/SimpleFactoryGenerator.svg)](https://www.nuget.org/packages/SimpleFactoryGenerator)\n\nEnglish | [中文](./README.zh-CN.md)\n\nThis library is used to assist in the implementation of the *Simple Factory Pattern* by automatically generating conditional branch structure in the factory class at **compile time**, thus solving the problem of the pattern violating the [\"open-close principle\"](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle).\n\n## 1. Why?\n\nThe introduction and use of the *Simple Factory Pattern* will not be described here. One of the drawbacks of this pattern is the need to manually maintain a (potentially huge) conditional branch structure for creating concrete instances of a given enumeration type (including strings). As a result, the pattern violates the \"open to extensions, closed to modifications\" design principle, which is solved by this library. The idea is very simple: design principles are a set of rules of thumb to facilitate the maintenance of code by *Humans*, to compensate for some of the limitations of human thinking. Therefore, there is no violation of the *Design Principles* by simply leaving this difficult-to-maintain part of the code to the **compiler**.\n\n## 2. How?\n\nLet's start by looking at a simple factory implemented in the traditional way, in order to facilitate comparison of the parts replaced by this library.\n\n```csharp\npublic interface IProduct\n{\n}\n\npublic class Product1 : IProduct\n{\n}\n\npublic class Product2 : IProduct\n{\n}\n\npublic class SimpleFactory\n{\n    public IProduct Create(string type)\n    {\n        // Here is the branch judgment that violates the open-close principle,\n        // for example, when adding `Product3`, you need to manually add\n        // a branch here (`\"product_c\" =\u003e new Product3(),`).\n        return type switch\n        {\n            \"product_a\" =\u003e new Product1(),\n            \"product_b\" =\u003e new Product2(),\n            _ =\u003e throw new IndexOutOfRangeException(),\n        }\n    }\n}\n\n// Using\n\nvar factory = new SimpleFactory();\nIProduct product = factory.Create(\"product_a\");\n```\n\nAfter using this library, the writing of `SimpleFactory` will be omitted and instead, a `ProductAttribute\u003cK, T\u003e` needs to be declared on the concrete `Product` type. You have already noticed: the Attribute uses generics, which requires [C# 11](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/generics-and-attributes) support, for which you need to use Visual Studio 2022 and configure it in the `*.csproj` file: `\u003cLangVersion\u003epreview\u003c/LangVersion\u003e`.\n\n\u003e If your project cannot be configured for C# 11 or uses Visual Studio 2022, which prevents you from **directly** using Generic-Attribute, you can refer to section 3.1 and customize an Attribute to inherit from `ProductAttribute\u003cK, T\u003e` (the Generic-Attribute definitions were allowed before C# 11, just not directly available).\n\n```csharp\n// It can also be an abstract class, or a normal class, and it is not mandatory to be an interface.\npublic interface IProduct\n{\n}\n\n[Product\u003cstring, IProduct\u003e(\"product_a\")]\npublic class Product1 : IProduct\n{\n}\n\n[Product\u003cstring, IProduct\u003e(\"product_b\")]\npublic class Product2 : IProduct\n{\n}\n\n// Using\n\n// The SimpleFactory static class are provided by this library.\nvar factory = SimpleFactory\n    .For\u003cstring, IProduct\u003e()\n    // .WithCache() is optional decorator\n    // that caches created instances (i.e. instances with the same key are created multiple times\n    // and the same instance is returned.)\n    .WithCache();\nIProduct product = factory.Create(\"product_a\");\n```\n\n## 3. Advanced\n\nIt's not really that advanced, it's such a simple requirement, what are you expecting? :)\n\n### 3.1 Custom Attribute\n\nIf you think the `ProductAttribute\u003cK, T\u003e` declaration too long, too ugly, too cumbersome (or can't use C# 11's Generic-Attribute syntax), you can customize an Attribute to inherit it.\n\n```csharp\npublic class FruitAttribute : ProductAttribute\u003cstring, IProduct\u003e\n{\n    public FruitAttribute(string name) : base(name)\n    {\n    }\n}\n\n[Fruit(\"apple\")]\npublic class Apple : IProduct\n{\n}\n```\n\n### 3.2 Mapping to multiple target interfaces\n\nFor the same concrete product type, it is allowed to supply to multiple different target interfaces.\n\n```csharp\n[Animal(\"mouse\")]\n[Food(\"duck_neck\")]\npublic class Mouse : IAnimal, IFood\n{\n}\n\n// Using\nvar animalFactory = SimpleFactory.For\u003cstring, IAnimal\u003e();\nIProduct mouse = animalFactory.Create(\"mouse\");\n\nvar foodFactory = SimpleFactory.For\u003cstring, IFood\u003e();\nIProduct duckNeck = foodFactory.Create(\"duck_neck\");\n```\n\n### 3.3 Passing arguments to the constructor\n\nIf a constructor of type `Product` has parameters, they can be passed as follows:\n\n```csharp\n_ = factory.Create(\"key\", arg1, arg2, ...);\n```\n\nSince it is often not possible to determine the constructor that creates the type when using `factory`, it is recommended that all `Product` constructors have a consistent argument list.\n\nIf there is a need to make the constructor arguments for each `Product` indeterminate, it is recommended that it be created in an Ioc container:\n\n```csharp\nvar factory = SimpleFactory\n    .For\u003cKey, Product\u003e()\n    .WithCreator((type, args) =\u003e (Product)container.Resolve(type, args));\n\n_ = factory.Create(key);\n```\n\nNote: If you use `.WithCreator()` after `.WithCache()`, it will cause the previous cache to be cleared.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdingpingzhang%2Fsimplefactorygenerator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdingpingzhang%2Fsimplefactorygenerator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdingpingzhang%2Fsimplefactorygenerator/lists"}