diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..50be806 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# +# These owners will be the default owners for everything in the repo. +# +* @TGWolf + diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5ffa3a8 --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,75 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behaviour that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behaviour by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behaviour and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behaviour. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behaviour may be +reported by contacting the project team at . All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the +[Contributor Covenant](https://www.contributor-covenant.org), version 1.4, +available at + +For answers to common questions about this code of conduct, see + diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..145a864 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,14 @@ +# Contributing + +Please refer to the +[contributing](https://github.com/WolfSoftware/contributing) +documentation. + +## Important + +ALL commits must be signed to ensure the identity of the developer, any pull +requests that are made with unsigned commits will be rejected as a matter of +course. + +> This project has a [code of conduct](CODE_OF_CONDUCT.md). By interacting +with this repository, organization, or community you agree to abide by its terms. diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b7a1e2f --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# Funding +# https://help.github.com/en/github/administering-a-repository/displaying-a-sponsor-button-in-your-repository + +github: [WolfSoftware,TGWolf] diff --git a/.github/ISSUE_TEMPLATE/ask_question.yml b/.github/ISSUE_TEMPLATE/ask_question.yml new file mode 100644 index 0000000..30ec360 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ask_question.yml @@ -0,0 +1,22 @@ +name: Ask a question +description: If you don't have a specific issue or bug to report you can still ask us questions and we will do our best to answer them +title: "[Question]: " +labels: ["type: question", "state: triage"] +assignees: + - tgwolf +body: + - type: textarea + id: question + attributes: + label: What is your question? + description: Please give us time to review your question and formulate an answer. + validations: + required: true + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/DevelopersToolbox/patterns/blob/master/.github/CODE_OF_CONDUCT.md) + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..b30c21f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,58 @@ +name: Report a bug +description: Found a bug? Let us knonw what the issue is and we will attempt to fix it +title: "[Bug]: " +labels: ["type: bug", "state: triage"] +assignees: + - tgwolf +body: + - type: textarea + id: what-happened + attributes: + label: What happened? + description: A clear and concise description of what the bug is. + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected behavior + description: A clear and concise description of what you expected to happen. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: How do we reproduct the bug? + description: What are the steps we need to take to reproduce the behavior? + validations: + required: true + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: false + - type: textarea + id: screenshoots + attributes: + label: Screeenshots + description: Upload any screenshots that might help demonstrate the bug. + validations: + required: false + - type: textarea + id: additional-information + attributes: + label: Additional information + description: Please provide any additional information that you think will help us to resolve this bug. + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/DevelopersToolbox/patterns/blob/master/.github/CODE_OF_CONDUCT.md) + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..cabd7e8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Support us + url: https://ko-fi.com/wolfsoftware + about: Show your support + - name: Visit our website + url: https://wolfsoftware.com/ + about: Visit the Wolf Software website and see what else we do and what services we offer diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..22b9754 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,43 @@ +name: Request a new feature +description: Got an idea for a new feature? Let us know what you want and we will see if we can add it +title: "[Feature Request]: " +labels: ["type: feature", "state: triage"] +assignees: + - tgwolf +body: + - type: textarea + id: releated-to + attributes: + label: Is your feature request related to a problem? + description: A clear and concise description of what the problem is. E.g. I'm always frustrated when ... + validations: + required: true + - type: textarea + id: suggested-solution + attributes: + label: Suggested Solution + description: A clear and concise description of what you want to see implemented. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Describe alternatives you've considered + description: A clear and concise description of any alternative solutions or features you've considered. + validations: + required: true + - type: textarea + id: additional-information + attributes: + label: Additional information + description: Please provide any additional information that you think will help us to resolve this bug. + validations: + required: false + - type: checkboxes + id: terms + attributes: + label: Code of Conduct + description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/DevelopersToolbox/patterns/blob/master/.github/CODE_OF_CONDUCT.md) + options: + - label: I agree to follow this project's Code of Conduct + required: true diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..0e99242 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,36 @@ +# Thank You + +Thanks for submitting a pull request! Please provide enough information so that +others can review your pull request: + +## Summary + + + +This PR fixes/implements the following **bugs/features** + +* [ ] Bug 1 +* [ ] Feature 1 +* [ ] Breaking changes + + + +Explain the **motivation** for making this change. What existing problem does +the pull request solve? + + + +## Test plan (required) + +Demonstrate the code is solid. Example: The exact commands you ran and their +output, screenshots help greatly. + + + +## Closing issues + + +Fixes # diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..b46843b --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,40 @@ +# Security Policies and Procedures + +This document outlines security procedures and general policies for this project. + +* [Reporting a Bug](#reporting-a-bug) +* [Disclosure Policy](#disclosure-policy) +* [Comments on this Policy](#comments-on-this-policy) + +## Reporting a Bug + +We take **ALL** security related bugs and issues very seriously. + +If you think you have identified a security related issue, please +[report it immediately](mailto:disclose@wolfsoftware.com) and include +the word "SECURITY" in the subject line. If you are not sure, don’t worry. +Better safe than sorry – just send an email. + +* Please provide as much information as you can. +* Please do not open issues related to any security concerns publicly. +* Please do not include anyone else on the disclosure email. + +Report security bugs in third-party modules to the person or team maintaining +the module. + +## Disclosure Policy + +When a security report is received, we will carry out the following steps: + +* Confirm the problem and determine the affected versions. +* Audit code to find any potential similar problems. +* Prepare fixes for all releases still under maintenance. These fixes will be + released as fast as possible. + +We will endeavour to keep you informed of the progress towards a fix and full +announcement, and may ask for additional information or guidance. + +## Comments on this Policy + +If you have suggestions on how this process could be improved please submit a +pull request. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..49d4678 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "04:00" + open-pull-requests-limit: 10 + commit-message: + prefix: "chore:" + labels: + - "dependabot: ecosystem : github actions" + - "dependabot: dependencies" + assignees: + - "TGWolf" diff --git a/.github/scripts/check-jobs.sh b/.github/scripts/check-jobs.sh new file mode 100755 index 0000000..e35587c --- /dev/null +++ b/.github/scripts/check-jobs.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# This script receives a JSON string containing job results and checks for any failures. + +# Check if jq is available +if ! command -v jq &> /dev/null; then + echo "jq could not be found, please install jq to run this script." + exit 1 +fi + +# Read the JSON string from the first script argument +job_results_json=$1 + +# Check if the job results JSON is not empty +if [[ -z "$job_results_json" ]]; then + echo "No job results JSON provided." + exit 1 +fi + +# Set default state +failed_jobs=false + +# Use jq to parse the JSON and check each job's result +while IFS= read -r line; do + job_name=$(echo "$line" | awk '{print $1}') + result=$(echo "$line" | awk '{print $3}') + + if [ "$result" != "success" ]; then + echo "$job_name failed." + failed_jobs=true + else + echo "$job_name succeed." + fi +done <<< "$( echo "$job_results_json" | jq -r 'to_entries[] | "\(.key) result: \(.value.result)"' )" + +if [ "$failed_jobs" = true ] ; then + exit 1 +fi diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 0000000..9797a38 --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,48 @@ +name: CI/CD Pipeline + +on: + push: + branches-ignore: + - 'dependabot/**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + + pull_request: + branches: + - '**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Perform ShellCheck Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/shellcheck/master/pipeline.sh) + + cicd-pipeline: + if: always() + name: CI/CD Pipeline + needs: + - shellcheck + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/citation-validation.yml b/.github/workflows/citation-validation.yml new file mode 100644 index 0000000..6021822 --- /dev/null +++ b/.github/workflows/citation-validation.yml @@ -0,0 +1,84 @@ +name: Citation Validation + +on: + push: + branches-ignore: + - 'dependabot/**' + paths: + - 'CITATION.cff' + pull_request: + branches: + - '**' + paths: + - 'CITATION.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + get-ruby-version: + name: Get Latest Ruby Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Version + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # v0.1.3 + id: get-language-versions + with: + language: "ruby" + highest-only: true + remove-patch-version: true + + awesomebot: + name: Awesomebot + needs: get-ruby-version + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Setup Ruby ${{ needs.get-ruby-version.outputs.version }} + uses: ruby/setup-ruby@af43264f2b94cc8451805dc51af7408f01de6471 # v1.182.0 + with: + ruby-version: ${{ needs.get-ruby-version.outputs.version }} + + - name: Perform Awesomebot Analysis + env: + FLAGS: "default" + WHITELIST: "https://img.shields.io" + INCLUDE_FILES: "CITATION.cff" + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/awesomebot/master/pipeline.sh) + + validate-citation-file: + name: Validate CITATION.cff + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Validate CITATION.cff + uses: citation-file-format/cffconvert-github-action@4cf11baa70a673bfdf9dad0acc7ee33b3f4b6084 # v2.0.0 + with: + args: "--validate" + + citation-validation-pipeline: + if: always() + name: Citation Validation Pipeline + needs: + - awesomebot + - validate-citation-file + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/delete-old-workflow-runs.yml b/.github/workflows/delete-old-workflow-runs.yml new file mode 100644 index 0000000..d314dd3 --- /dev/null +++ b/.github/workflows/delete-old-workflow-runs.yml @@ -0,0 +1,75 @@ +name: Delete Old Workflow Runs + +on: + workflow_dispatch: + inputs: + days: + description: 'Number of days to retain workflow runs.' + required: true + default: '30' + minimum-runs: + description: 'The minimum number of runs to keep for each workflow.' + required: true + default: '6' + + schedule: + - cron: '19 2 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + DAYS: 30 + MINIMUM_RUNS: 6 + +permissions: read-all + +jobs: + set-runtime-values: + name: Set Runtime Values + runs-on: ubuntu-latest + outputs: + days: ${{ steps.set-output-defaults.outputs.days }} + minimum-runs: ${{ steps.set-output-defaults.outputs.minimum-runs }} + + steps: + - name: Set Runtime Values + id: set-output-defaults + run: | + echo "days=${{ github.event.inputs.days || env.DAYS }}" >> "${GITHUB_OUTPUT}" + echo "minimum-runs=${{ github.event.inputs.minimum-runs || env.MINIMUM_RUNS }}" >> "${GITHUB_OUTPUT}" + + delete-old-workflows: + name: Delete Old Workflow Runs + runs-on: ubuntu-latest + permissions: + actions: write + needs: + - set-runtime-values + + steps: + - name: Delete Old Workflow Runs + uses: Mattraks/delete-workflow-runs@39f0bbed25d76b34de5594dceab824811479e5de # v2.0.6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + repository: ${{ github.repository }} + retain_days: ${{ needs.set-runtime-values.outputs.days }} + keep_minimum_runs: ${{ needs.set-runtime-values.outputs.minimum-runs }} + + slack-workflow-status: + if: always() + name: Slack Post Workflow Notification + needs: + - delete-old-workflows + runs-on: ubuntu-latest + + steps: + - name: Slack Workflow Notifications + if: ${{ github.event_name == 'schedule' && needs.delete-old-workflows.result != 'success'}} + uses: Gamesight/slack-workflow-status@68bf00d0dbdbcb206c278399aa1ef6c14f74347a # v1.3.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + include_jobs: on-failure + include_commit_message: true diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..247ba9d --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,53 @@ +name: Dependabot Pull Request Approve & Merge + +on: pull_request + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + dependabot: + name: Dependabot + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Fetch Metadata + id: dependabot-metadata + uses: dependabot/fetch-metadata@5e5f99653a5b510e8555840e80cbf1514ad4af38 # v2.1.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Approve PR + if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' || steps.dependabot-metadata.outputs.update-type == 'version-update:semver-minor' }} + run: | + gh pr review --approve "${PR_URL}" -b "I'm **approving** this pull request because it includes a patch or minor update" + gh pr edit "${PR_URL}" --add-label "dependabot: auto approve" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Auto-Merge Non-Major Updates + if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-patch' || steps.dependabot-metadata.outputs.update-type == 'version-update:semver-minor' }} + run: | + gh pr comment "${PR_URL}" --body "I'm automatically merging this PR because it includes a patch or minor update" + gh pr merge --auto --squash --delete-branch "${PR_URL}" + gh pr edit "${PR_URL}" --add-label "dependabot: auto merge" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Comment & Label Major Updates + if: ${{ steps.dependabot-metadata.outputs.update-type == 'version-update:semver-major' }} + run: | + gh pr comment "${PR_URL}" --body "I'm **NOT** automatically merging this PR because it includes a major update of a dependency" + gh pr edit "${PR_URL}" --add-label "dependabot: manual merge" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/document-validation.yml b/.github/workflows/document-validation.yml new file mode 100644 index 0000000..1ba8fed --- /dev/null +++ b/.github/workflows/document-validation.yml @@ -0,0 +1,102 @@ +name: Documentation Validation + +on: + push: + branches-ignore: + - 'dependabot/**' + paths: + - '**/*.md' + pull_request: + branches: + - '**' + paths: + - '**/*.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + get-node-version: + name: Get Latest Node Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Version + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # v0.1.3 + id: get-language-versions + with: + language: "node" + highest-only: true + remove-patch-version: true + + get-ruby-version: + name: Get Latest Ruby Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Version + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # v0.1.3 + id: get-language-versions + with: + language: "ruby" + highest-only: true + remove-patch-version: true + + awesomebot: + name: Awesomebot + needs: get-ruby-version + runs-on: ubuntu-latest + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Setup Ruby ${{ needs.get-ruby-version.outputs.version }} + uses: ruby/setup-ruby@af43264f2b94cc8451805dc51af7408f01de6471 # v1.182.0 + with: + ruby-version: ${{ needs.get-ruby-version.outputs.version }} + + - name: Perform Awesomebot Analysis + env: + FLAGS: "default" + WHITELIST: "https://img.shields.io" + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/awesomebot/master/pipeline.sh) + + markdown-lint: + name: Markdown Lint + needs: get-node-version + runs-on: ubuntu-latest + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Setup Node ${{ needs.get-node-version.outputs.version }} + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: ${{ needs.get-node-version.outputs.version }} + + - name: Perform Markdown Lint Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/markdown-lint/master/pipeline.sh) + env: + EXCLUDE_FILES: "README.md,CHANGELOG.md,docs/" + + repository-validation-pipeline: + if: always() + name: Documentation Validation Pipeline + needs: + - awesomebot + - markdown-lint + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/generate-release.yml b/.github/workflows/generate-release.yml new file mode 100644 index 0000000..99b97f2 --- /dev/null +++ b/.github/workflows/generate-release.yml @@ -0,0 +1,62 @@ +name: Generate a Release + +on: + push: + tags: + - 'v[0-9].[0-9]+.[0-9]+' + - '!v[0-9].[0-9]+.[0-9]+rc[0-9]+' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + set-release-version: + name: Set Release Version + runs-on: ubuntu-latest + outputs: + release-version: ${{ steps.set-release-version.outputs.release-version }} + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 + + - name: Set the Release Version + id: set-release-version + run: | + echo "release-version=${GITHUB_REF#refs/*/}" >> "${GITHUB_OUTPUT}" + + create-release: + name: Create a Release + permissions: + contents: write + runs-on: ubuntu-latest + needs: + - set-release-version + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 + + - name: Generate Changelog + uses: Bullrich/generate-release-changelog@6b60f004b4bf12ff271603dc32dbd261965ad2f2 # v2.0.2 + id: Changelog + env: + REPO: ${{ github.repository }} + + - name: Create a Release + id: create_release + uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag_name: ${{ github.ref }} + name: ${{ needs.set-release-version.outputs.release-version }} + body: ${{ steps.Changelog.outputs.changelog }} + draft: false + prerelease: false diff --git a/.github/workflows/generate-test-release.yml b/.github/workflows/generate-test-release.yml new file mode 100644 index 0000000..3b07ea7 --- /dev/null +++ b/.github/workflows/generate-test-release.yml @@ -0,0 +1,60 @@ +name: Generate a TEST Release + +on: + push: + tags: + - 'v[0-9].[0-9]+.[0-9]+rc[0-9]+' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + set-release-version: + name: Set Release Version + runs-on: ubuntu-latest + outputs: + release-version: ${{ steps.set-release-version.outputs.release-version }} + + steps: + - name: Checkout the repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 + + - name: Set the release version + id: set-release-version + run: echo "release-version=${GITHUB_REF#refs/*/}" >> "${GITHUB_OUTPUT}" + + create-release: + name: Create Release + permissions: + contents: write + runs-on: ubuntu-latest + needs: + - set-release-version + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + fetch-depth: 0 + + - name: Generate Changelog + uses: Bullrich/generate-release-changelog@6b60f004b4bf12ff271603dc32dbd261965ad2f2 # v2.0.2 + id: Changelog + env: + REPO: ${{ github.repository }} + + - name: Create a Release + id: create_release + uses: softprops/action-gh-release@a74c6b72af54cfa997e81df42d94703d6313a2d0 # v2.0.6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag_name: ${{ github.ref }} + name: ${{ needs.set-release-version.outputs.release-version }} + body: ${{ steps.Changelog.outputs.changelog }} + draft: false + prerelease: true diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 0000000..ddd7cd8 --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,27 @@ +name: Greetings + +on: + pull_request: + issues: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + greetings: + name: Handle Greetings + permissions: + issues: write + pull-requests: write + runs-on: ubuntu-latest + + steps: + - name: Handle Greetings + uses: actions/first-interaction@34f15e814fe48ac9312ccf29db4e74fa767cbab7 # v1.3.0 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: "Thank you for raising your first issue - all contributions to this project are welcome!" + pr-message: "Thank you for raising your first pull request - all contributions to this project are welcome!" diff --git a/.github/workflows/purge-deprecated-workflow-runs.yml b/.github/workflows/purge-deprecated-workflow-runs.yml new file mode 100644 index 0000000..09121fb --- /dev/null +++ b/.github/workflows/purge-deprecated-workflow-runs.yml @@ -0,0 +1,47 @@ +name: Purge Deprecated Workflow Runs + +on: + workflow_dispatch: + + schedule: + - cron: '31 5 * * 1' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + purge-obsolete-workflows: + name: Purge Deprecated Workflow Runs + permissions: + actions: write + runs-on: ubuntu-latest + + steps: + - name: Purge Deprecated Workflow Runs + uses: otto-de/purge-deprecated-workflow-runs@31a4e821d43e9a354cbd65845922c76e4b4b3633 # v 2.0.4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + remove-obsolete: true + remove-cancelled: true + remove-failed: true + remove-skipped: true + + slack-workflow-status: + if: always() + name: Slack Post Workflow Notification + needs: + - purge-obsolete-workflows + runs-on: ubuntu-latest + + steps: + - name: Slack Workflow Notifications + if: ${{ github.event_name == 'schedule' && needs.purge-obsolete-workflows.result != 'success'}} + uses: Gamesight/slack-workflow-status@68bf00d0dbdbcb206c278399aa1ef6c14f74347a # v1.3.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + include_jobs: on-failure + include_commit_message: true diff --git a/.github/workflows/repository-validation.yml b/.github/workflows/repository-validation.yml new file mode 100644 index 0000000..df45dd7 --- /dev/null +++ b/.github/workflows/repository-validation.yml @@ -0,0 +1,100 @@ +name: Repository Validation + +on: + push: + branches-ignore: + - 'dependabot/**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + pull_request: + branches: + - '**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + get-go-version: + name: Get Latest Go Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Versions + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # V0.1.3 + id: get-language-versions + with: + language: "go" + highest-only: true + remove-patch-version: true + + get-python-version: + name: Get Latest Python Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.get-language-versions.outputs.latest-versions }} + + steps: + - name: Get Required Versions + uses: ActionsToolbox/get-language-versions-action@446919617fd774095b5dd3ed71c39dd3fd0d8f4f # V0.1.3 + id: get-language-versions + with: + language: "python" + highest-only: true + remove-patch-version: true + + action-lint: + name: Action Lint + needs: get-go-version + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Setup Go ${{ needs.get-go-version.outputs.version }} + uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # V5.0.1 + with: + go-version: ${{ needs.get-go-version.outputs.version }} + + - name: Perform Action Lint Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/action-lint/master/pipeline.sh) + + yaml-lint: + name: YAML Lint + needs: get-python-version + runs-on: ubuntu-latest + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Set up Python ${{ needs.get-python-version.outputs.version }} + uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # V5.1.0 + with: + python-version: ${{ needs.get-python-version.outputs.version }} + + - name: Perform YAML Lint Analysis + run: bash <(curl -s https://raw.githubusercontent.com/CICDToolbox/yaml-lint/master/pipeline.sh) + + repository-validation-pipeline: + if: always() + name: Repository Validation Pipeline + needs: + - action-lint + - yaml-lint + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Check Job Statuses + run: .github/scripts/check-jobs.sh '${{ toJson(needs) }}' diff --git a/.github/workflows/security-hardening.yml b/.github/workflows/security-hardening.yml new file mode 100644 index 0000000..b24c32e --- /dev/null +++ b/.github/workflows/security-hardening.yml @@ -0,0 +1,33 @@ +name: Security Hardening + +on: + push: + branches-ignore: + - 'dependabot/**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + pull_request: + branches: + - '**' + paths-ignore: + - '**/*.md' + - '**/*.cff' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + security-hardening: + name: Harden Security + runs-on: ubuntu-latest + + steps: + - name: Checkout the Repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Ensure SHA Pinned Actions + uses: zgosalvez/github-actions-ensure-sha-pinned-actions@74606c30450304eee8660aae751818321754feb1 # v3.0.9 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..8b55fb5 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,57 @@ +name: Stale Issue & PR Handler + +on: + schedule: + - cron: '1 5 * * *' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: read-all + +jobs: + stale: + name: Handle Stale Issues & PRs + permissions: + contents: write + issues: write + pull-requests: write + runs-on: ubuntu-latest + + steps: + - name: Handle Stale Issues & PRs + uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 + id: stale-issues + with: + stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.' + close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.' + days-before-issue-stale: 30 + days-before-issue-close: 5 + stale-issue-label: 'state: stale' + close-issue-label: 'resolution: closed' + exempt-issue-labels: 'state: blocked,state: keep' + stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.' + close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.' + days-before-pr-stale: 45 + days-before-pr-close: 10 + stale-pr-label: 'state: stale' + close-pr-label: 'resolution: closed' + exempt-pr-labels: 'state: blocked,state: keep' + + slack-workflow-status: + if: always() + name: Slack Post Workflow Notification + needs: + - stale + runs-on: ubuntu-latest + + steps: + - name: Slack Workflow Notifications + if: ${{ github.event_name == 'schedule' && needs.stale.result != 'success'}} + uses: Gamesight/slack-workflow-status@68bf00d0dbdbcb206c278399aa1ef6c14f74347a # v1.3.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + include_jobs: on-failure + include_commit_message: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..20f4490 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +### Hard Coded Minimums ### +# +# Because I use a Mac +# +**/.DS-Store + +# +# Ignore Awesomebot output +# +**/ab-results* + diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..804e6c1 --- /dev/null +++ b/.yamllint @@ -0,0 +1,26 @@ +--- + +extends: default + +rules: + braces: + level: warning + max-spaces-inside: 1 + brackets: + level: warning + max-spaces-inside: 1 + colons: + level: warning + commas: + level: warning + comments-indentation: disable + document-start: disable + empty-lines: + level: warning + hyphens: + level: warning + indentation: + level: warning + indent-sequences: consistent + line-length: disable + truthy: disable diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..4f8a6c1 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,15 @@ +cff-version: 1.2.0 +message: If you use this software, please cite it using these metadata. +title: Design Patterns +abstract: A collection of different development patterns. +type: software +version: 0.1.0 +date-released: 2024-06-28 +repository-code: https://github.com/DevelopersToolbox/design-patterns +keywords: + - "Wolf Software" + - "Software" +license: MIT +authors: + - family-names: "Wolf" + orcid: "https://orcid.org/0009-0007-0983-2072" diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c14811f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © `2009-2024` `Wolf Software` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..aebd59f --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ + +

+ + DevelopersToolbox logo + +
+ + Github Build Status + + + License + + + Created + +
+ + Release + + + Released + + + Commits since release + +
+ + + + + + + + + + + + +

+ +## Overview + +Design patterns are standardized solutions to common problems encountered in software design. They represent best practices refined over time by +experienced developers and architects, providing proven techniques to address recurring challenges. Design patterns help streamline the development +process by offering templates and guidelines for solving specific design issues, thus promoting code reuse, efficiency, and consistency across projects. +They are not concrete implementations but rather conceptual frameworks that can be adapted to fit various scenarios and requirements. + +The primary purpose of using design patterns is to improve the overall architecture of a software system. By employing design patterns, developers can +create flexible, scalable, and maintainable code. These patterns help in managing complexity, ensuring that systems are easier to understand and extend. +Selecting the correct design pattern is crucial because it directly impacts the system's efficiency, readability, and adaptability. The wrong pattern can +lead to increased complexity, poor performance, and difficulties in maintenance. Therefore, understanding the specific problem at hand and the context in +which it occurs is essential for choosing the most appropriate design pattern, ultimately leading to more robust and effective software solutions. + +## The Patterns + +| Pattern | Description | +| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Adapter Pattern](docs/Adapter.md) | The adapter pattern allows incompatible interfaces to work together by converting the interface of one class into another expected by the client. | +| [Builder Pattern](docs/Builder.md) | The builder pattern simplifies the construction of complex objects by separating the construction process from the final representation. | +| [Chain of Responsibility Pattern](docs/Chain-of-Responsibility.md) | The chain of responsibility pattern delegates commands to a chain of processing objects, allowing multiple objects a chance to handle the request. | +| [Command Pattern](docs/Command.md) | The command pattern encapsulates a request as an object, allowing for parameterization, queuing, logging, and supporting undoable operations. | +| [Composite Pattern](docs/Composite.md) | The composite pattern allows composing objects into tree structures to represent part-whole hierarchies, treating individual objects and compositions uniformly. | +| [Decorator Pattern](docs/Decorator.md) | The decorator pattern dynamically adds behaviour to individual objects without affecting the behaviour of other objects from the same class. | +| [Facade Pattern](docs/Facade.md) | The facade pattern provides a simplified interface to a complex subsystem, making it easier for clients to interact with the system. | +| [Factory Pattern](docs/Factory.md) | The factory pattern defines an interface for creating objects but allows subclasses to alter the type of objects that will be created. | +| [Flyweight Pattern](docs/Flyweight.md) | The flyweight pattern reduces the cost of creating and managing a large number of similar objects by sharing as much data as possible. | +| [Iterator Pattern](docs/Iterator.md) | The iterator pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. | +| [Observer Pattern](docs/Observer.md) | The observer pattern defines a one-to-many dependency so that when one object changes state, all its dependents are notified and updated automatically. | +| [Proxy Pattern](docs/Proxy.md) | The proxy pattern provides a surrogate or placeholder for another object to control access to it, enhancing control over the underlying object. | +| [Singleton Pattern](docs/Singleton.md) | The singleton pattern ensures a class has only one instance and provides a global point of access to it, managing shared resources efficiently. | +| [Strategy Pattern](docs/Strategy.md) | The strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable, allowing the algorithm to vary independently from the clients that use it. | + +
+

diff --git a/docs/Adapter.md b/docs/Adapter.md new file mode 100644 index 0000000..c4a1695 --- /dev/null +++ b/docs/Adapter.md @@ -0,0 +1,211 @@ +## Adapter Pattern + +The adapter pattern is a structural design pattern that enables objects with incompatible interfaces to collaborate. It acts as a bridge, +converting the interface of a class into one that a client expects. This pattern is particularly useful when integrating new components into +an existing system, as it allows for the reuse of existing functionalities without modifying their source code. By wrapping an existing class +with a new interface, the adapter pattern ensures compatibility and seamless integration, promoting flexibility and extensibility in software design. + +### Go Example + +```go +package main + +import "fmt" + +// Target is the interface that the client expects. +type Target interface { + Request() string +} + +// Adaptee contains some useful behavior, but its interface is incompatible +// with the existing client code. +type Adaptee struct{} + +// SpecificRequest is the incompatible method of Adaptee. +func (a *Adaptee) SpecificRequest() string { + return ".eetpadA eht fo roivaheb laicepS" +} + +// Adapter makes Adaptee's interface compatible with the Target interface. +type Adapter struct { + adaptee *Adaptee +} + +// NewAdapter constructs a new Adapter. +func NewAdapter(adaptee *Adaptee) *Adapter { + return &Adapter{adaptee} +} + +// Request calls the incompatible method and adapts the result. +func (a *Adapter) Request() string { + runes := []rune(a.adaptee.SpecificRequest()) + for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { + runes[i], runes[j] = runes[j], runes[i] + } + return fmt.Sprintf("Adapter: (TRANSLATED) %s", string(runes)) +} + +func clientCode(target Target) { + fmt.Println(target.Request()) +} + +func main() { + adaptee := &Adaptee{} + adapter := NewAdapter(adaptee) + clientCode(adapter) +} +``` + +### Perl Example + +```perl +package Target; +sub new { bless {}, shift } +sub request { "Target: The default target's behavior." } + +package Adaptee; +sub new { bless {}, shift } +sub specific_request { ".eetpadA eht fo roivaheb laicepS" } + +package Adapter; +sub new { + my ($class, $adaptee) = @_; + bless { adaptee => $adaptee }, $class; +} + +sub request { + my $self = shift; + my $result = reverse $self->{adaptee}->specific_request(); + return "Adapter: (TRANSLATED) $result"; +} + +package main; +sub client_code { + my $target = shift; + print $target->request, "\n"; +} + +my $target = Target->new; +client_code($target); + +my $adaptee = Adaptee->new; +my $adapter = Adapter->new($adaptee); +client_code($adapter); +``` + +### Python Example + +```python +class Target: + def request(self): + return "Target: The default target's behavior." + + +class Adaptee: + def specific_request(self): + return ".eetpadA eht fo roivaheb laicepS" + + +class Adapter(Target): + def __init__(self, adaptee): + self.adaptee = adaptee + + def request(self): + return f"Adapter: (TRANSLATED) {self.adaptee.specific_request()[::-1]}" + + +def client_code(target): + print(target.request()) + + +if __name__ == "__main__": + target = Target() + client_code(target) + + adaptee = Adaptee() + adapter = Adapter(adaptee) + client_code(adapter) +``` + +### Ruby Example + +```ruby +class Target + def request + "Target: The default target's behavior." + end +end + +class Adaptee + def specific_request + ".eetpadA eht fo roivaheb laicepS" + end +end + +class Adapter < Target + def initialize(adaptee) + @adaptee = adaptee + end + + def request + "Adapter: (TRANSLATED) #{reverse_string(@adaptee.specific_request)}" + end + + def reverse_string(str) + str.reverse + end +end + +def client_code(target) + puts target.request +end + +target = Target.new +client_code(target) + +adaptee = Adaptee.new +adapter = Adapter.new(adaptee) +client_code(adapter) +``` + +### Rust Example + +```rust +trait Target { + fn request(&self) -> String; +} + +struct Adaptee; + +impl Adaptee { + fn specific_request(&self) -> String { + ".eetpadA eht fo roivaheb laicepS".to_string() + } +} + +struct Adapter { + adaptee: Adaptee, +} + +impl Adapter { + fn new(adaptee: Adaptee) -> Self { + Adapter { adaptee } + } +} + +impl Target for Adapter { + fn request(&self) -> String { + format!("Adapter: (TRANSLATED) {}", self.adaptee.specific_request().chars().rev().collect::()) + } +} + +fn client_code(target: &dyn Target) { + println!("{}", target.request()); +} + +fn main() { + let adaptee = Adaptee; + let adapter = Adapter::new(adaptee); + client_code(&adapter); +} +``` diff --git a/docs/Builder.md b/docs/Builder.md new file mode 100644 index 0000000..1600071 --- /dev/null +++ b/docs/Builder.md @@ -0,0 +1,237 @@ +## Builder Pattern + +The builder pattern is a creational design pattern that helps in constructing complex objects step by step. Unlike other creational patterns +that construct objects in a single step, the builder pattern allows for the creation of a complex object through a step-by-step approach, separating +the construction process from its representation. This separation ensures that the same construction process can create different representations of +the object. The builder pattern is particularly useful for creating objects that require numerous parameters or configuration options, ensuring that +the constructed object is always in a valid state. + +### Go Example + +```go +package main + +import "fmt" + +type House struct { + Walls string + Roof string + Windows string +} + +type HouseBuilder struct { + house House +} + +func (b *HouseBuilder) BuildWalls(walls string) *HouseBuilder { + b.house.Walls = walls + return b +} + +func (b *HouseBuilder) BuildRoof(roof string) *HouseBuilder { + b.house.Roof = roof + return b +} + +func (b *HouseBuilder) BuildWindows(windows string) *HouseBuilder { + b.house.Windows = windows + return b +} + +func (b *HouseBuilder) GetHouse() House { + return b.house +} + +// Usage +func main() { + builder := &HouseBuilder{} + house := builder.BuildWalls("brick").BuildRoof("tile").BuildWindows("double-glazed").GetHouse() + fmt.Println(house) +} +``` + +### Perl Example + +```perl +package House; +sub new { + my $class = shift; + my $self = { + walls => undef, + roof => undef, + windows => undef, + }; + bless $self, $class; + return $self; +} + +package HouseBuilder; +sub new { + my $class = shift; + my $self = { house => House->new() }; + bless $self, $class; + return $self; +} + +sub build_walls { + my ($self, $walls) = @_; + $self->{house}->{walls} = $walls; + return $self; +} + +sub build_roof { + my ($self, $roof) = @_; + $self->{house}->{roof} = $roof; + return $self; +} + +sub build_windows { + my ($self, $windows) = @_; + $self->{house}->{windows} = $windows; + return $self; +} + +sub get_house { + my $self = shift; + return $self->{house}; +} + +# Usage +my $builder = HouseBuilder->new(); +my $house = $builder->build_walls("brick")->build_roof("tile")->build_windows("double-glazed")->get_house(); +print "$_ => $house->{$_}\n" for keys %$house; +``` + +### Python Example + +```python +class House: + def __init__(self): + self.walls = None + self.roof = None + self.windows = None + +class HouseBuilder: + def __init__(self): + self.house = House() + + def build_walls(self, walls): + self.house.walls = walls + return self + + def build_roof(self, roof): + self.house.roof = roof + return self + + def build_windows(self, windows): + self.house.windows = windows + return self + + def get_house(self): + return self.house + +# Usage +builder = HouseBuilder() +house = builder.build_walls("brick").build_roof("tile").build_windows("double-glazed").get_house() +print(house.__dict__) +``` + +### Ruby Example + +```ruby +class House + attr_accessor :walls, :roof, :windows + + def initialize + @walls = nil + @roof = nil + @windows = nil + end +end + +class HouseBuilder + def initialize + @house = House.new + end + + def build_walls(walls) + @house.walls = walls + self + end + + def build_roof(roof) + @house.roof = roof + self + end + + def build_windows(windows) + @house.windows = windows + self + end + + def get_house + @house + end +end + +# Usage +builder = HouseBuilder.new +house = builder.build_walls("brick").build_roof("tile").build_windows("double-glazed").get_house +puts house.inspect +``` + +### Rust Example + +```rust +struct House { + walls: Option, + roof: Option, + windows: Option, +} + +struct HouseBuilder { + house: House, +} + +impl HouseBuilder { + fn new() -> Self { + HouseBuilder { + house: House { + walls: None, + roof: None, + windows: None, + }, + } + } + + fn build_walls(mut self, walls: &str) -> Self { + self.house.walls = Some(walls.to_string()); + self + } + + fn build_roof(mut self, roof: &str) -> Self { + self.house.roof = Some(roof.to_string()); + self + } + + fn build_windows(mut self, windows: &str) -> Self { + self.house.windows = Some(windows.to_string()); + self + } + + fn get_house(self) -> House { + self.house + } +} + +// Usage +fn main() { + let builder = HouseBuilder::new(); + let house = builder + .build_walls("brick") + .build_roof("tile") + .build_windows("double-glazed") + .get_house(); + println!("{:?}", house); +} +``` diff --git a/docs/Chain-of-Responsibility.md b/docs/Chain-of-Responsibility.md new file mode 100644 index 0000000..9242736 --- /dev/null +++ b/docs/Chain-of-Responsibility.md @@ -0,0 +1,205 @@ +## Chain of Responsibility Pattern + +The chain of responsibility pattern is a behavioural design pattern that allows an object to send a command without knowing which object will +handle it. This pattern creates a chain of processing objects, where each object in the chain can handle the request, pass it along the chain, +or decline to handle it. This approach promotes loose coupling between the sender and receivers of a request, enabling flexibility in assigning +responsibilities. The chain of responsibility pattern is particularly useful for scenarios where multiple objects might handle a request, and +the specific handler isn't known in advance. + +### Go Example + +```go +package main + +import "fmt" + +type Handler interface { + Handle(request string) +} + +type ConcreteHandler1 struct { + successor Handler +} + +func (h *ConcreteHandler1) Handle(request string) { + if request == "one" { + fmt.Println("ConcreteHandler1 handled the request") + } else if h.successor != nil { + h.successor.Handle(request) + } +} + +type ConcreteHandler2 struct { + successor Handler +} + +func (h *ConcreteHandler2) Handle(request string) { + if request == "two" { + fmt.Println("ConcreteHandler2 handled the request") + } else if h.successor != nil { + h.successor.Handle(request) + } +} + +func main() { + handlerChain := &ConcreteHandler1{ + successor: &ConcreteHandler2{}, + } + + handlerChain.Handle("two") + handlerChain.Handle("one") +} +``` + +### Perl Example + +```perl +package Handler; +sub new { + my ($class, $successor) = @_; + return bless { successor => $successor }, $class; +} + +sub handle { + die "NotImplementedError"; +} + +package ConcreteHandler1; +use parent -norequire, 'Handler'; + +sub handle { + my ($self, $request) = @_; + if ($request eq 'one') { + print "ConcreteHandler1 handled the request\n"; + } elsif ($self->{successor}) { + $self->{successor}->handle($request); + } +} + +package ConcreteHandler2; +use parent -norequire, 'Handler'; + +sub handle { + my ($self, $request) = @_; + if ($request eq 'two') { + print "ConcreteHandler2 handled the request\n"; + } elsif ($self->{successor}) { + $self->{successor}->handle($request); + } +} + +my $handler_chain = ConcreteHandler1->new(ConcreteHandler2->new); +$handler_chain->handle('two'); +$handler_chain->handle('one'); +``` + +### Python Example + +```python +class Handler: + def __init__(self, successor=None): + self.successor = successor + + def handle(self, request): + raise NotImplementedError("Must provide implementation in subclass.") + +class ConcreteHandler1(Handler): + def handle(self, request): + if request == "one": + print("ConcreteHandler1 handled the request") + elif self.successor: + self.successor.handle(request) + +class ConcreteHandler2(Handler): + def handle(self, request): + if request == "two": + print("ConcreteHandler2 handled the request") + elif self.successor: + self.successor.handle(request) + +handler_chain = ConcreteHandler1(ConcreteHandler2()) +handler_chain.handle("two") +handler_chain.handle("one") +``` + +### Ruby Example + +```ruby +class Handler + def initialize(successor = nil) + @successor = successor + end + + def handle(request) + raise 'NotImplementedError' + end +end + +class ConcreteHandler1 < Handler + def handle(request) + if request == 'one' + puts 'ConcreteHandler1 handled the request' + elsif @successor + @successor.handle(request) + end + end +end + +class ConcreteHandler2 < Handler + def handle(request) + if request == 'two' + puts 'ConcreteHandler2 handled the request' + elsif @successor + @successor.handle(request) + end + end +end + +handler_chain = ConcreteHandler1.new(ConcreteHandler2.new) +handler_chain.handle('two') +handler_chain.handle('one') +``` + +### Rust Example + +```rust +trait Handler { + fn handle(&self, request: &str); +} + +struct ConcreteHandler1 { + successor: Option>, +} + +impl Handler for ConcreteHandler1 { + fn handle(&self, request: &str) { + if request == "one" { + println!("ConcreteHandler1 handled the request"); + } else if let Some(ref successor) = self.successor { + successor.handle(request); + } + } +} + +struct ConcreteHandler2 { + successor: Option>, +} + +impl Handler for ConcreteHandler2 { + fn handle(&self, request: &str) { + if request == "two" { + println!("ConcreteHandler2 handled the request"); + } else if let Some(ref successor) = self.successor { + successor.handle(request); + } + } +} + +fn main() { + let handler_chain = ConcreteHandler1 { + successor: Some(Box::new(ConcreteHandler2 { successor: None })), + }; + + handler_chain.handle("two"); + handler_chain.handle("one"); +``` diff --git a/docs/Command.md b/docs/Command.md new file mode 100644 index 0000000..2fc31d0 --- /dev/null +++ b/docs/Command.md @@ -0,0 +1,245 @@ +## Command Pattern + +The command pattern is a behavioural design pattern that turns a request into a stand-alone object containing all the information about the +request. This transformation allows for the parameterization of clients with different requests, queuing or logging requests, and supporting +undoable operations. The command pattern decouples the object that invokes the operation from the one that knows how to perform it. In this +pattern, a command interface declares a method for executing a particular action, while concrete command classes implement this interface, +binding the receiver and the specific actions. This separation allows commands to be composed, stored, and executed dynamically, offering +extensive control over operation flow and facilitating features like undo/redo and transaction management. + +### Go Example + +```go +package main + +import "fmt" + +type Command interface { + Execute() +} + +type LightOnCommand struct { + light Light +} + +func (c *LightOnCommand) Execute() { + c.light.On() +} + +type Light struct{} + +func (l Light) On() { + fmt.Println("Light is on") +} + +type RemoteControl struct { + command Command +} + +func (r *RemoteControl) SetCommand(command Command) { + r.command = command +} + +func (r *RemoteControl) PressButton() { + r.command.Execute() +} + +func main() { + light := Light{} + lightOnCommand := LightOnCommand{light: light} + remote := RemoteControl{} + remote.SetCommand(&lightOnCommand) + remote.PressButton() +} +``` + +### Perl Example + +```perl +package Command; +sub execute { } + +package LightOnCommand; +our @ISA = qw(Command); + +sub new { + my ($class, $light) = @_; + my $self = { light => $light }; + bless $self, $class; + return $self; +} + +sub execute { + my $self = shift; + $self->{light}->on(); +} + +package Light; + +sub on { + print "Light is on\n"; +} + +package RemoteControl; + +sub new { + my $class = shift; + my $self = {}; + bless $self, $class; + return $self; +} + +sub set_command { + my ($self, $command) = @_; + $self->{command} = $command; +} + +sub press_button { + my $self = shift; + $self->{command}->execute(); +} + +# Usage +my $light = Light->new(); +my $light_on_command = LightOnCommand->new($light); +my $remote = RemoteControl->new(); +$remote->set_command($light_on_command); +$remote->press_button();``` + +### Python Example + +```python +class Command: + def execute(self): + pass + +class LightOnCommand(Command): + def __init__(self, light): + self.light = light + + def execute(self): + self.light.on() + +class Light: + def on(self): + print("Light is on") + +class RemoteControl: + def __init__(self): + self.command = None + + def set_command(self, command): + self.command = command + + def press_button(self): + self.command.execute() + +# Usage +light = Light() +light_on_command = LightOnCommand(light) +remote = RemoteControl() +remote.set_command(light_on_command) +remote.press_button() +``` + +### Ruby Example + +```ruby +class Command + def execute + end +end + +class LightOnCommand < Command + def initialize(light) + @light = light + end + + def execute + @light.on + end +end + +class Light + def on + puts "Light is on" + end +end + +class RemoteControl + def set_command(command) + @command = command + end + + def press_button + @command.execute + end +end + +# Usage +light = Light.new +light_on_command = LightOnCommand.new(light) +remote = RemoteControl.new +remote.set_command(light_on_command) +remote.press_button +``` + +### Rust Example + +```rust +trait Command { + fn execute(&self); +} + +struct LightOnCommand { + light: Light, +} + +impl LightOnCommand { + fn new(light: Light) -> Self { + LightOnCommand { light } + } +} + +impl Command for LightOnCommand { + fn execute(&self) { + self.light.on(); + } +} + +struct Light; + +impl Light { + fn on(&self) { + println!("Light is on"); + } +} + +struct RemoteControl { + command: Option>, +} + +impl RemoteControl { + fn new() -> Self { + RemoteControl { command: None } + } + + fn set_command(&mut self, command: Box) { + self.command = Some(command); + } + + fn press_button(&self) { + if let Some(ref command) = self.command { + command.execute(); + } + } +} + +fn main() { + let light = Light; + let light_on_command = LightOnCommand::new(light); + let mut remote = RemoteControl::new(); + remote.set_command(Box::new(light_on_command)); + remote.press_button(); +} +``` diff --git a/docs/Composite.md b/docs/Composite.md new file mode 100644 index 0000000..e874344 --- /dev/null +++ b/docs/Composite.md @@ -0,0 +1,235 @@ +## Composite Pattern + +The composite pattern is a structural design pattern that enables you to compose objects into tree structures to represent part-whole hierarchies. +This pattern allows clients to treat individual objects and compositions of objects uniformly, simplifying the client code. By implementing a common +interface for both simple and complex objects, the composite pattern makes it easier to work with recursive structures and complex hierarchies. +This approach promotes flexibility and reusability, enabling the creation of complex structures with ease and facilitating operations on these structures. + + +### Go Example + +```go +package main + +import "fmt" + +type Component interface { + Operation() +} + +type Leaf struct{} + +func (l *Leaf) Operation() { + fmt.Println("Leaf operation") +} + +type Composite struct { + children []Component +} + +func (c *Composite) Add(component Component) { + c.children = append(c.children, component) +} + +func (c *Composite) Remove(component Component) { + for i, child := range c.children { + if child == component { + c.children = append(c.children[:i], c.children[i+1:]...) + break + } + } +} + +func (c *Composite) Operation() { + fmt.Println("Composite operation") + for _, child := range c.children { + child.Operation() + } +} + +func main() { + leaf1 := &Leaf{} + leaf2 := &Leaf{} + + composite := &Composite{} + composite.Add(leaf1) + composite.Add(leaf2) + composite.Operation() +} +``` + +### Perl Example + +```perl +package Component; +sub new { bless {}, shift } +sub operation { die "Abstract method\n" } + +package Leaf; +our @ISA = qw(Component); +sub operation { print "Leaf operation\n" } + +package Composite; +our @ISA = qw(Component); +sub new { + my $class = shift; + my $self = { _children => [] }; + bless $self, $class; + return $self; +} + +sub add { + my ($self, $component) = @_; + push @{$self->{_children}}, $component; +} + +sub remove { + my ($self, $component) = @_; + @{$self->{_children}} = grep { $_ != $component } @{$self->{_children}}; +} + +sub operation { + my $self = shift; + print "Composite operation\n"; + $_->operation for @{$self->{_children}}; +} + +# Usage +my $leaf1 = Leaf->new; +my $leaf2 = Leaf->new; +my $composite = Composite->new; +$composite->add($leaf1); +$composite->add($leaf2); +$composite->operation; +``` + +### Python Example + +```python +class Component: + def operation(self): + pass + +class Leaf(Component): + def operation(self): + print("Leaf operation") + +class Composite(Component): + def __init__(self): + self._children = [] + + def add(self, component): + self._children.append(component) + + def remove(self, component): + self._children.remove(component) + + def operation(self): + print("Composite operation") + for child in self._children: + child.operation() + +# Usage +leaf1 = Leaf() +leaf2 = Leaf() +composite = Composite() +composite.add(leaf1) +composite.add(leaf2) +composite.operation() +``` + +### Ruby Example + +```ruby +class Component + def operation + raise NotImplementedError, 'Subclasses must override this method' + end +end + +class Leaf < Component + def operation + puts 'Leaf operation' + end +end + +class Composite < Component + def initialize + @children = [] + end + + def add(component) + @children << component + end + + def remove(component) + @children.delete(component) + end + + def operation + puts 'Composite operation' + @children.each(&:operation) + end +end + +# Usage +leaf1 = Leaf.new +leaf2 = Leaf.new +composite = Composite.new +composite.add(leaf1) +composite.add(leaf2) +composite.operation +``` + +### Rust Example + +```rust +trait Component { + fn operation(&self); +} + +struct Leaf; + +impl Component for Leaf { + fn operation(&self) { + println!("Leaf operation"); + } +} + +struct Composite { + children: Vec>, +} + +impl Composite { + fn new() -> Composite { + Composite { children: Vec::new() } + } + + fn add(&mut self, component: Box) { + self.children.push(component); + } + + fn remove(&mut self, component: *const dyn Component) { + self.children.retain(|&ref c| &**c as *const _ != component); + } +} + +impl Component for Composite { + fn operation(&self) { + println!("Composite operation"); + for child in &self.children { + child.operation(); + } + } +} + +fn main() { + let leaf1 = Box::new(Leaf); + let leaf2 = Box::new(Leaf); + + let mut composite = Composite::new(); + composite.add(leaf1); + composite.add(leaf2); + composite.operation(); +} +``` diff --git a/docs/Decorator.md b/docs/Decorator.md new file mode 100644 index 0000000..8c60ba9 --- /dev/null +++ b/docs/Decorator.md @@ -0,0 +1,210 @@ +## Decorator Pattern + +The decorator pattern is a structural design pattern that allows behaviour to be added to individual objects dynamically, without affecting the +behaviour of other objects from the same class. It provides a flexible alternative to subclassing for extending functionality. In this pattern, +a decorator class wraps the original class, adding new behaviour while maintaining the original class's interface. This approach promotes the +open/closed principle, allowing classes to be open for extension but closed for modification. The decorator pattern is particularly useful for +adding features to objects in a flexible and reusable manner. + + +### Go Example + +```go +package main + +import "fmt" + +type Coffee interface { + Cost() float64 +} + +type BasicCoffee struct{} + +func (c BasicCoffee) Cost() float64 { + return 5.0 +} + +type MilkDecorator struct { + coffee Coffee +} + +func (m MilkDecorator) Cost() float64 { + return m.coffee.Cost() + 1.0 +} + +type SugarDecorator struct { + coffee Coffee +} + +func (s SugarDecorator) Cost() float64 { + return s.coffee.Cost() + 0.5 +} + +func main() { + coffee := BasicCoffee{} + fmt.Println(coffee.Cost()) // 5.0 + + coffeeWithMilk := MilkDecorator{coffee: coffee} + fmt.Println(coffeeWithMilk.Cost()) // 6.0 + + coffeeWithMilkAndSugar := SugarDecorator{coffee: coffeeWithMilk} + fmt.Println(coffeeWithMilkAndSugar.Cost()) // 6.5 +} +``` + +### Perl Example + +```perl +package Coffee; +sub new { bless {}, shift } +sub cost { 5 } + +package MilkDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 1 } + +package SugarDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 0.5 } + +# Usage +my $coffee = Coffee->new; +print $coffee->cost . "\n"; # 5 + +my $coffee_with_milk = MilkDecorator->new($coffee); +print $coffee_with_milk->cost . "\n"; # 6 + +my $coffee_with_milk_and_sugar = SugarDecorator->new($coffee_with_milk); +print $coffee_with_milk_and_sugar->cost . "\n"; # 6.5 +``` + +### Python Example + +```python +class Coffee: + def cost(self): + return 5 + +class MilkDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 1 + +class SugarDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 0.5 + +# Usage +coffee = Coffee() +print(coffee.cost()) # 5 + +coffee_with_milk = MilkDecorator(coffee) +print(coffee_with_milk.cost()) # 6 + +coffee_with_milk_and_sugar = SugarDecorator(coffee_with_milk) +print(coffee_with_milk_and_sugar.cost()) # 6.5 +``` + +### Ruby Example + +```ruby +class Coffee + def cost + 5 + end +end + +class MilkDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 1 + end +end + +class SugarDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 0.5 + end +end + +# Usage +coffee = Coffee.new +puts coffee.cost # 5 + +coffee_with_milk = MilkDecorator.new(coffee) +puts coffee_with_milk.cost # 6 + +coffee_with_milk_and_sugar = SugarDecorator.new(coffee_with_milk) +puts coffee_with_milk_and_sugar.cost # 6.5 +``` + +### Rust Example + +```rust +trait Coffee { + fn cost(&self) -> f64; +} + +struct BasicCoffee; + +impl Coffee for BasicCoffee { + fn cost(&self) -> f64 { + 5.0 + } +} + +struct MilkDecorator { + coffee: Box, +} + +impl MilkDecorator { + fn new(coffee: Box) -> MilkDecorator { + MilkDecorator { coffee } + } +} + +impl Coffee for MilkDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 1.0 + } +} + +struct SugarDecorator { + coffee: Box, +} + +impl SugarDecorator { + fn new(coffee: Box) -> SugarDecorator { + SugarDecorator { coffee } + } +} + +impl Coffee for SugarDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 0.5 + } +} + +fn main() { + let coffee = BasicCoffee; + println!("{}", coffee.cost()); // 5.0 + + let coffee_with_milk = MilkDecorator::new(Box::new(coffee)); + println!("{}", coffee_with_milk.cost()); // 6.0 + + let coffee_with_milk_and_sugar = SugarDecorator::new(Box::new(coffee_with_milk)); + println!("{}", coffee_with_milk_and_sugar.cost()); // 6.5 +} +``` diff --git a/docs/Decorator/README.md b/docs/Decorator/README.md new file mode 100644 index 0000000..b93d06e --- /dev/null +++ b/docs/Decorator/README.md @@ -0,0 +1,37 @@ +### Pattern + + +
+Go Example + +```go +``` +
+ +
+Perl Example + +```perl +``` +
+ +
+Python Example + +```python +``` +
+ +
+Ruby Example + +```ruby +``` +
+ +
+Rust Example + +```rust +``` +
diff --git a/docs/Decorator/decorator.go b/docs/Decorator/decorator.go new file mode 100644 index 0000000..83e3c0c --- /dev/null +++ b/docs/Decorator/decorator.go @@ -0,0 +1,205 @@ +Sure, here are examples of the decorator pattern in Python, Ruby, Perl, Rust, and Go. + +### Python + +```python +class Coffee: + def cost(self): + return 5 + +class MilkDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 1 + +class SugarDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 0.5 + +# Usage +coffee = Coffee() +print(coffee.cost()) # 5 + +coffee_with_milk = MilkDecorator(coffee) +print(coffee_with_milk.cost()) # 6 + +coffee_with_milk_and_sugar = SugarDecorator(coffee_with_milk) +print(coffee_with_milk_and_sugar.cost()) # 6.5 +``` + +### Ruby + +```ruby +class Coffee + def cost + 5 + end +end + +class MilkDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 1 + end +end + +class SugarDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 0.5 + end +end + +# Usage +coffee = Coffee.new +puts coffee.cost # 5 + +coffee_with_milk = MilkDecorator.new(coffee) +puts coffee_with_milk.cost # 6 + +coffee_with_milk_and_sugar = SugarDecorator.new(coffee_with_milk) +puts coffee_with_milk_and_sugar.cost # 6.5 +``` + +### Perl + +```perl +package Coffee; +sub new { bless {}, shift } +sub cost { 5 } + +package MilkDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 1 } + +package SugarDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 0.5 } + +# Usage +my $coffee = Coffee->new; +print $coffee->cost . "\n"; # 5 + +my $coffee_with_milk = MilkDecorator->new($coffee); +print $coffee_with_milk->cost . "\n"; # 6 + +my $coffee_with_milk_and_sugar = SugarDecorator->new($coffee_with_milk); +print $coffee_with_milk_and_sugar->cost . "\n"; # 6.5 +``` + +### Rust + +```rust +trait Coffee { + fn cost(&self) -> f64; +} + +struct BasicCoffee; + +impl Coffee for BasicCoffee { + fn cost(&self) -> f64 { + 5.0 + } +} + +struct MilkDecorator { + coffee: Box, +} + +impl MilkDecorator { + fn new(coffee: Box) -> MilkDecorator { + MilkDecorator { coffee } + } +} + +impl Coffee for MilkDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 1.0 + } +} + +struct SugarDecorator { + coffee: Box, +} + +impl SugarDecorator { + fn new(coffee: Box) -> SugarDecorator { + SugarDecorator { coffee } + } +} + +impl Coffee for SugarDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 0.5 + } +} + +fn main() { + let coffee = BasicCoffee; + println!("{}", coffee.cost()); // 5.0 + + let coffee_with_milk = MilkDecorator::new(Box::new(coffee)); + println!("{}", coffee_with_milk.cost()); // 6.0 + + let coffee_with_milk_and_sugar = SugarDecorator::new(Box::new(coffee_with_milk)); + println!("{}", coffee_with_milk_and_sugar.cost()); // 6.5 +} +``` + +### Go + +```go +package main + +import "fmt" + +type Coffee interface { + Cost() float64 +} + +type BasicCoffee struct{} + +func (c BasicCoffee) Cost() float64 { + return 5.0 +} + +type MilkDecorator struct { + coffee Coffee +} + +func (m MilkDecorator) Cost() float64 { + return m.coffee.Cost() + 1.0 +} + +type SugarDecorator struct { + coffee Coffee +} + +func (s SugarDecorator) Cost() float64 { + return s.coffee.Cost() + 0.5 +} + +func main() { + coffee := BasicCoffee{} + fmt.Println(coffee.Cost()) // 5.0 + + coffeeWithMilk := MilkDecorator{coffee: coffee} + fmt.Println(coffeeWithMilk.Cost()) // 6.0 + + coffeeWithMilkAndSugar := SugarDecorator{coffee: coffeeWithMilk} + fmt.Println(coffeeWithMilkAndSugar.Cost()) // 6.5 +} +``` + +These examples demonstrate how the decorator pattern can be implemented in various languages to extend the functionality of objects dynamically. diff --git a/docs/Decorator/decorator.pl b/docs/Decorator/decorator.pl new file mode 100644 index 0000000..83e3c0c --- /dev/null +++ b/docs/Decorator/decorator.pl @@ -0,0 +1,205 @@ +Sure, here are examples of the decorator pattern in Python, Ruby, Perl, Rust, and Go. + +### Python + +```python +class Coffee: + def cost(self): + return 5 + +class MilkDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 1 + +class SugarDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 0.5 + +# Usage +coffee = Coffee() +print(coffee.cost()) # 5 + +coffee_with_milk = MilkDecorator(coffee) +print(coffee_with_milk.cost()) # 6 + +coffee_with_milk_and_sugar = SugarDecorator(coffee_with_milk) +print(coffee_with_milk_and_sugar.cost()) # 6.5 +``` + +### Ruby + +```ruby +class Coffee + def cost + 5 + end +end + +class MilkDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 1 + end +end + +class SugarDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 0.5 + end +end + +# Usage +coffee = Coffee.new +puts coffee.cost # 5 + +coffee_with_milk = MilkDecorator.new(coffee) +puts coffee_with_milk.cost # 6 + +coffee_with_milk_and_sugar = SugarDecorator.new(coffee_with_milk) +puts coffee_with_milk_and_sugar.cost # 6.5 +``` + +### Perl + +```perl +package Coffee; +sub new { bless {}, shift } +sub cost { 5 } + +package MilkDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 1 } + +package SugarDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 0.5 } + +# Usage +my $coffee = Coffee->new; +print $coffee->cost . "\n"; # 5 + +my $coffee_with_milk = MilkDecorator->new($coffee); +print $coffee_with_milk->cost . "\n"; # 6 + +my $coffee_with_milk_and_sugar = SugarDecorator->new($coffee_with_milk); +print $coffee_with_milk_and_sugar->cost . "\n"; # 6.5 +``` + +### Rust + +```rust +trait Coffee { + fn cost(&self) -> f64; +} + +struct BasicCoffee; + +impl Coffee for BasicCoffee { + fn cost(&self) -> f64 { + 5.0 + } +} + +struct MilkDecorator { + coffee: Box, +} + +impl MilkDecorator { + fn new(coffee: Box) -> MilkDecorator { + MilkDecorator { coffee } + } +} + +impl Coffee for MilkDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 1.0 + } +} + +struct SugarDecorator { + coffee: Box, +} + +impl SugarDecorator { + fn new(coffee: Box) -> SugarDecorator { + SugarDecorator { coffee } + } +} + +impl Coffee for SugarDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 0.5 + } +} + +fn main() { + let coffee = BasicCoffee; + println!("{}", coffee.cost()); // 5.0 + + let coffee_with_milk = MilkDecorator::new(Box::new(coffee)); + println!("{}", coffee_with_milk.cost()); // 6.0 + + let coffee_with_milk_and_sugar = SugarDecorator::new(Box::new(coffee_with_milk)); + println!("{}", coffee_with_milk_and_sugar.cost()); // 6.5 +} +``` + +### Go + +```go +package main + +import "fmt" + +type Coffee interface { + Cost() float64 +} + +type BasicCoffee struct{} + +func (c BasicCoffee) Cost() float64 { + return 5.0 +} + +type MilkDecorator struct { + coffee Coffee +} + +func (m MilkDecorator) Cost() float64 { + return m.coffee.Cost() + 1.0 +} + +type SugarDecorator struct { + coffee Coffee +} + +func (s SugarDecorator) Cost() float64 { + return s.coffee.Cost() + 0.5 +} + +func main() { + coffee := BasicCoffee{} + fmt.Println(coffee.Cost()) // 5.0 + + coffeeWithMilk := MilkDecorator{coffee: coffee} + fmt.Println(coffeeWithMilk.Cost()) // 6.0 + + coffeeWithMilkAndSugar := SugarDecorator{coffee: coffeeWithMilk} + fmt.Println(coffeeWithMilkAndSugar.Cost()) // 6.5 +} +``` + +These examples demonstrate how the decorator pattern can be implemented in various languages to extend the functionality of objects dynamically. diff --git a/docs/Decorator/decorator.py b/docs/Decorator/decorator.py new file mode 100644 index 0000000..83e3c0c --- /dev/null +++ b/docs/Decorator/decorator.py @@ -0,0 +1,205 @@ +Sure, here are examples of the decorator pattern in Python, Ruby, Perl, Rust, and Go. + +### Python + +```python +class Coffee: + def cost(self): + return 5 + +class MilkDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 1 + +class SugarDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 0.5 + +# Usage +coffee = Coffee() +print(coffee.cost()) # 5 + +coffee_with_milk = MilkDecorator(coffee) +print(coffee_with_milk.cost()) # 6 + +coffee_with_milk_and_sugar = SugarDecorator(coffee_with_milk) +print(coffee_with_milk_and_sugar.cost()) # 6.5 +``` + +### Ruby + +```ruby +class Coffee + def cost + 5 + end +end + +class MilkDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 1 + end +end + +class SugarDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 0.5 + end +end + +# Usage +coffee = Coffee.new +puts coffee.cost # 5 + +coffee_with_milk = MilkDecorator.new(coffee) +puts coffee_with_milk.cost # 6 + +coffee_with_milk_and_sugar = SugarDecorator.new(coffee_with_milk) +puts coffee_with_milk_and_sugar.cost # 6.5 +``` + +### Perl + +```perl +package Coffee; +sub new { bless {}, shift } +sub cost { 5 } + +package MilkDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 1 } + +package SugarDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 0.5 } + +# Usage +my $coffee = Coffee->new; +print $coffee->cost . "\n"; # 5 + +my $coffee_with_milk = MilkDecorator->new($coffee); +print $coffee_with_milk->cost . "\n"; # 6 + +my $coffee_with_milk_and_sugar = SugarDecorator->new($coffee_with_milk); +print $coffee_with_milk_and_sugar->cost . "\n"; # 6.5 +``` + +### Rust + +```rust +trait Coffee { + fn cost(&self) -> f64; +} + +struct BasicCoffee; + +impl Coffee for BasicCoffee { + fn cost(&self) -> f64 { + 5.0 + } +} + +struct MilkDecorator { + coffee: Box, +} + +impl MilkDecorator { + fn new(coffee: Box) -> MilkDecorator { + MilkDecorator { coffee } + } +} + +impl Coffee for MilkDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 1.0 + } +} + +struct SugarDecorator { + coffee: Box, +} + +impl SugarDecorator { + fn new(coffee: Box) -> SugarDecorator { + SugarDecorator { coffee } + } +} + +impl Coffee for SugarDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 0.5 + } +} + +fn main() { + let coffee = BasicCoffee; + println!("{}", coffee.cost()); // 5.0 + + let coffee_with_milk = MilkDecorator::new(Box::new(coffee)); + println!("{}", coffee_with_milk.cost()); // 6.0 + + let coffee_with_milk_and_sugar = SugarDecorator::new(Box::new(coffee_with_milk)); + println!("{}", coffee_with_milk_and_sugar.cost()); // 6.5 +} +``` + +### Go + +```go +package main + +import "fmt" + +type Coffee interface { + Cost() float64 +} + +type BasicCoffee struct{} + +func (c BasicCoffee) Cost() float64 { + return 5.0 +} + +type MilkDecorator struct { + coffee Coffee +} + +func (m MilkDecorator) Cost() float64 { + return m.coffee.Cost() + 1.0 +} + +type SugarDecorator struct { + coffee Coffee +} + +func (s SugarDecorator) Cost() float64 { + return s.coffee.Cost() + 0.5 +} + +func main() { + coffee := BasicCoffee{} + fmt.Println(coffee.Cost()) // 5.0 + + coffeeWithMilk := MilkDecorator{coffee: coffee} + fmt.Println(coffeeWithMilk.Cost()) // 6.0 + + coffeeWithMilkAndSugar := SugarDecorator{coffee: coffeeWithMilk} + fmt.Println(coffeeWithMilkAndSugar.Cost()) // 6.5 +} +``` + +These examples demonstrate how the decorator pattern can be implemented in various languages to extend the functionality of objects dynamically. diff --git a/docs/Decorator/decorator.rb b/docs/Decorator/decorator.rb new file mode 100644 index 0000000..83e3c0c --- /dev/null +++ b/docs/Decorator/decorator.rb @@ -0,0 +1,205 @@ +Sure, here are examples of the decorator pattern in Python, Ruby, Perl, Rust, and Go. + +### Python + +```python +class Coffee: + def cost(self): + return 5 + +class MilkDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 1 + +class SugarDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 0.5 + +# Usage +coffee = Coffee() +print(coffee.cost()) # 5 + +coffee_with_milk = MilkDecorator(coffee) +print(coffee_with_milk.cost()) # 6 + +coffee_with_milk_and_sugar = SugarDecorator(coffee_with_milk) +print(coffee_with_milk_and_sugar.cost()) # 6.5 +``` + +### Ruby + +```ruby +class Coffee + def cost + 5 + end +end + +class MilkDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 1 + end +end + +class SugarDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 0.5 + end +end + +# Usage +coffee = Coffee.new +puts coffee.cost # 5 + +coffee_with_milk = MilkDecorator.new(coffee) +puts coffee_with_milk.cost # 6 + +coffee_with_milk_and_sugar = SugarDecorator.new(coffee_with_milk) +puts coffee_with_milk_and_sugar.cost # 6.5 +``` + +### Perl + +```perl +package Coffee; +sub new { bless {}, shift } +sub cost { 5 } + +package MilkDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 1 } + +package SugarDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 0.5 } + +# Usage +my $coffee = Coffee->new; +print $coffee->cost . "\n"; # 5 + +my $coffee_with_milk = MilkDecorator->new($coffee); +print $coffee_with_milk->cost . "\n"; # 6 + +my $coffee_with_milk_and_sugar = SugarDecorator->new($coffee_with_milk); +print $coffee_with_milk_and_sugar->cost . "\n"; # 6.5 +``` + +### Rust + +```rust +trait Coffee { + fn cost(&self) -> f64; +} + +struct BasicCoffee; + +impl Coffee for BasicCoffee { + fn cost(&self) -> f64 { + 5.0 + } +} + +struct MilkDecorator { + coffee: Box, +} + +impl MilkDecorator { + fn new(coffee: Box) -> MilkDecorator { + MilkDecorator { coffee } + } +} + +impl Coffee for MilkDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 1.0 + } +} + +struct SugarDecorator { + coffee: Box, +} + +impl SugarDecorator { + fn new(coffee: Box) -> SugarDecorator { + SugarDecorator { coffee } + } +} + +impl Coffee for SugarDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 0.5 + } +} + +fn main() { + let coffee = BasicCoffee; + println!("{}", coffee.cost()); // 5.0 + + let coffee_with_milk = MilkDecorator::new(Box::new(coffee)); + println!("{}", coffee_with_milk.cost()); // 6.0 + + let coffee_with_milk_and_sugar = SugarDecorator::new(Box::new(coffee_with_milk)); + println!("{}", coffee_with_milk_and_sugar.cost()); // 6.5 +} +``` + +### Go + +```go +package main + +import "fmt" + +type Coffee interface { + Cost() float64 +} + +type BasicCoffee struct{} + +func (c BasicCoffee) Cost() float64 { + return 5.0 +} + +type MilkDecorator struct { + coffee Coffee +} + +func (m MilkDecorator) Cost() float64 { + return m.coffee.Cost() + 1.0 +} + +type SugarDecorator struct { + coffee Coffee +} + +func (s SugarDecorator) Cost() float64 { + return s.coffee.Cost() + 0.5 +} + +func main() { + coffee := BasicCoffee{} + fmt.Println(coffee.Cost()) // 5.0 + + coffeeWithMilk := MilkDecorator{coffee: coffee} + fmt.Println(coffeeWithMilk.Cost()) // 6.0 + + coffeeWithMilkAndSugar := SugarDecorator{coffee: coffeeWithMilk} + fmt.Println(coffeeWithMilkAndSugar.Cost()) // 6.5 +} +``` + +These examples demonstrate how the decorator pattern can be implemented in various languages to extend the functionality of objects dynamically. diff --git a/docs/Decorator/decorator.rs b/docs/Decorator/decorator.rs new file mode 100644 index 0000000..83e3c0c --- /dev/null +++ b/docs/Decorator/decorator.rs @@ -0,0 +1,205 @@ +Sure, here are examples of the decorator pattern in Python, Ruby, Perl, Rust, and Go. + +### Python + +```python +class Coffee: + def cost(self): + return 5 + +class MilkDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 1 + +class SugarDecorator: + def __init__(self, coffee): + self._coffee = coffee + + def cost(self): + return self._coffee.cost() + 0.5 + +# Usage +coffee = Coffee() +print(coffee.cost()) # 5 + +coffee_with_milk = MilkDecorator(coffee) +print(coffee_with_milk.cost()) # 6 + +coffee_with_milk_and_sugar = SugarDecorator(coffee_with_milk) +print(coffee_with_milk_and_sugar.cost()) # 6.5 +``` + +### Ruby + +```ruby +class Coffee + def cost + 5 + end +end + +class MilkDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 1 + end +end + +class SugarDecorator + def initialize(coffee) + @coffee = coffee + end + + def cost + @coffee.cost + 0.5 + end +end + +# Usage +coffee = Coffee.new +puts coffee.cost # 5 + +coffee_with_milk = MilkDecorator.new(coffee) +puts coffee_with_milk.cost # 6 + +coffee_with_milk_and_sugar = SugarDecorator.new(coffee_with_milk) +puts coffee_with_milk_and_sugar.cost # 6.5 +``` + +### Perl + +```perl +package Coffee; +sub new { bless {}, shift } +sub cost { 5 } + +package MilkDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 1 } + +package SugarDecorator; +sub new { my ($class, $coffee) = @_; bless { coffee => $coffee }, $class } +sub cost { my $self = shift; $self->{coffee}->cost + 0.5 } + +# Usage +my $coffee = Coffee->new; +print $coffee->cost . "\n"; # 5 + +my $coffee_with_milk = MilkDecorator->new($coffee); +print $coffee_with_milk->cost . "\n"; # 6 + +my $coffee_with_milk_and_sugar = SugarDecorator->new($coffee_with_milk); +print $coffee_with_milk_and_sugar->cost . "\n"; # 6.5 +``` + +### Rust + +```rust +trait Coffee { + fn cost(&self) -> f64; +} + +struct BasicCoffee; + +impl Coffee for BasicCoffee { + fn cost(&self) -> f64 { + 5.0 + } +} + +struct MilkDecorator { + coffee: Box, +} + +impl MilkDecorator { + fn new(coffee: Box) -> MilkDecorator { + MilkDecorator { coffee } + } +} + +impl Coffee for MilkDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 1.0 + } +} + +struct SugarDecorator { + coffee: Box, +} + +impl SugarDecorator { + fn new(coffee: Box) -> SugarDecorator { + SugarDecorator { coffee } + } +} + +impl Coffee for SugarDecorator { + fn cost(&self) -> f64 { + self.coffee.cost() + 0.5 + } +} + +fn main() { + let coffee = BasicCoffee; + println!("{}", coffee.cost()); // 5.0 + + let coffee_with_milk = MilkDecorator::new(Box::new(coffee)); + println!("{}", coffee_with_milk.cost()); // 6.0 + + let coffee_with_milk_and_sugar = SugarDecorator::new(Box::new(coffee_with_milk)); + println!("{}", coffee_with_milk_and_sugar.cost()); // 6.5 +} +``` + +### Go + +```go +package main + +import "fmt" + +type Coffee interface { + Cost() float64 +} + +type BasicCoffee struct{} + +func (c BasicCoffee) Cost() float64 { + return 5.0 +} + +type MilkDecorator struct { + coffee Coffee +} + +func (m MilkDecorator) Cost() float64 { + return m.coffee.Cost() + 1.0 +} + +type SugarDecorator struct { + coffee Coffee +} + +func (s SugarDecorator) Cost() float64 { + return s.coffee.Cost() + 0.5 +} + +func main() { + coffee := BasicCoffee{} + fmt.Println(coffee.Cost()) // 5.0 + + coffeeWithMilk := MilkDecorator{coffee: coffee} + fmt.Println(coffeeWithMilk.Cost()) // 6.0 + + coffeeWithMilkAndSugar := SugarDecorator{coffee: coffeeWithMilk} + fmt.Println(coffeeWithMilkAndSugar.Cost()) // 6.5 +} +``` + +These examples demonstrate how the decorator pattern can be implemented in various languages to extend the functionality of objects dynamically. diff --git a/docs/Facade.md b/docs/Facade.md new file mode 100644 index 0000000..a0c7deb --- /dev/null +++ b/docs/Facade.md @@ -0,0 +1,260 @@ +## Facade Pattern + +The facade pattern is a structural design pattern that provides a simplified interface to a complex subsystem. It hides the complexities of the +system and provides a single point of access to its functionalities. By offering a high-level interface, the facade pattern makes it easier for +clients to interact with the system, reducing the learning curve and improving usability. This pattern is particularly useful when dealing with +complex systems or libraries, as it promotes loose coupling between the client and the subsystem, enhancing maintainability and scalability. + +### Go Example + +```go +package main + +import "fmt" + +type CPU struct{} + +func (c *CPU) Freeze() { + fmt.Println("Freezing processor...") +} + +func (c *CPU) Jump(position int) { + fmt.Printf("Jumping to position %d...\n", position) +} + +func (c *CPU) Execute() { + fmt.Println("Executing instructions...") +} + +type Memory struct{} + +func (m *Memory) Load(position int, data string) { + fmt.Printf("Loading %s into position %d...\n", data, position) +} + +type HardDrive struct{} + +func (hd *HardDrive) Read(lba, size int) string { + return fmt.Sprintf("Read %d bytes from LBA %d.", size, lba) +} + +type ComputerFacade struct { + cpu *CPU + memory *Memory + hardDrive *HardDrive +} + +func NewComputerFacade() *ComputerFacade { + return &ComputerFacade{ + cpu: &CPU{}, + memory: &Memory{}, + hardDrive: &HardDrive{}, + } +} + +func (cf *ComputerFacade) Start() { + cf.cpu.Freeze() + cf.memory.Load(0, cf.hardDrive.Read(100, 1024)) + cf.cpu.Jump(0) + cf.cpu.Execute() +} + +func main() { + computer := NewComputerFacade() + computer.Start() +} +``` + +### Perl Example + +```perl +package CPU; +sub new { bless {}, shift } +sub freeze { print "Freezing processor...\n"; } +sub jump { my ($self, $position) = @_; print "Jumping to position $position...\n"; } +sub execute { print "Executing instructions...\n"; } + +package Memory; +sub new { bless {}, shift } +sub load { my ($self, $position, $data) = @_; print "Loading $data into position $position...\n"; } + +package HardDrive; +sub new { bless {}, shift } +sub read { my ($self, $lba, $size) = @_; return "Read $size bytes from LBA $lba."; } + +package ComputerFacade; +sub new { + my $class = shift; + bless { + cpu => CPU->new, + memory => Memory->new, + hard_drive => HardDrive->new + }, $class; +} +sub start { + my $self = shift; + $self->{cpu}->freeze; + $self->{memory}->load(0, $self->{hard_drive}->read(100, 1024)); + $self->{cpu}->jump(0); + $self->{cpu}->execute; +} + +# Client code +my $computer = ComputerFacade->new; +$computer->start; +``` + +### Python Example + +```python +class CPU: + def freeze(self): + print("Freezing processor...") + + def jump(self, position): + print(f"Jumping to position {position}...") + + def execute(self): + print("Executing instructions...") + + +class Memory: + def load(self, position, data): + print(f"Loading {data} into position {position}...") + + +class HardDrive: + def read(self, lba, size): + return f"Read {size} bytes from LBA {lba}." + + +class ComputerFacade: + def __init__(self): + self.cpu = CPU() + self.memory = Memory() + self.hard_drive = HardDrive() + + def start(self): + self.cpu.freeze() + self.memory.load(0, self.hard_drive.read(100, 1024)) + self.cpu.jump(0) + self.cpu.execute() + + +# Client code +computer = ComputerFacade() +computer.start() +``` + +### Ruby Example + +```ruby +class CPU + def freeze + puts "Freezing processor..." + end + + def jump(position) + puts "Jumping to position #{position}..." + end + + def execute + puts "Executing instructions..." + end +end + +class Memory + def load(position, data) + puts "Loading #{data} into position #{position}..." + end +end + +class HardDrive + def read(lba, size) + "Read #{size} bytes from LBA #{lba}." + end +end + +class ComputerFacade + def initialize + @cpu = CPU.new + @memory = Memory.new + @hard_drive = HardDrive.new + end + + def start + @cpu.freeze + @memory.load(0, @hard_drive.read(100, 1024)) + @cpu.jump(0) + @cpu.execute + end +end + +# Client code +computer = ComputerFacade.new +computer.start +``` + +### Rust Example + +```rust +struct CPU; + +impl CPU { + fn freeze(&self) { + println!("Freezing processor..."); + } + + fn jump(&self, position: u32) { + println!("Jumping to position {}...", position); + } + + fn execute(&self) { + println!("Executing instructions..."); + } +} + +struct Memory; + +impl Memory { + fn load(&self, position: u32, data: &str) { + println!("Loading {} into position {}...", data, position); + } +} + +struct HardDrive; + +impl HardDrive { + fn read(&self, lba: u32, size: u32) -> String { + format!("Read {} bytes from LBA {}.", size, lba) + } +} + +struct ComputerFacade { + cpu: CPU, + memory: Memory, + hard_drive: HardDrive, +} + +impl ComputerFacade { + fn new() -> Self { + Self { + cpu: CPU, + memory: Memory, + hard_drive: HardDrive, + } + } + + fn start(&self) { + self.cpu.freeze(); + self.memory.load(0, &self.hard_drive.read(100, 1024)); + self.cpu.jump(0); + self.cpu.execute(); + } +} + +fn main() { + let computer = ComputerFacade::new(); + computer.start(); +} +``` diff --git a/docs/Factory.md b/docs/Factory.md new file mode 100644 index 0000000..d25317a --- /dev/null +++ b/docs/Factory.md @@ -0,0 +1,207 @@ +## Factory Pattern + +The factory pattern is a creational design pattern that provides an interface for creating objects while allowing subclasses to decide which +class to instantiate. This pattern promotes loose coupling by eliminating the need to bind application-specific classes into the code. The +factory pattern encapsulates the object creation process, enabling flexibility and reusability. It is particularly useful when the exact type +of the object cannot be determined until runtime, as it provides a way to defer instantiation to subclasses, ensuring that the appropriate +object is created for a given situation. + +### Go Example + +```go +package main + +import "fmt" + +type Animal interface { + Speak() string +} + +type Dog struct{} +func (d Dog) Speak() string { + return "Woof!" +} + +type Cat struct{} +func (c Cat) Speak() string { + return "Meow!" +} + +type AnimalFactory struct{} +func (af AnimalFactory) GetAnimal(animalType string) Animal { + switch animalType { + case "dog": + return Dog{} + case "cat": + return Cat{} + default: + return nil + } +} + +func main() { + factory := AnimalFactory{} + animal := factory.GetAnimal("dog") + if animal != nil { + fmt.Println(animal.Speak()) + } else { + fmt.Println("Unknown animal type") + } +} +``` + +### Perl Example + +```perl +package Animal; +sub speak { + die "You must implement the speak method"; +} + +package Dog; +use parent 'Animal'; +sub speak { + return "Woof!"; +} + +package Cat; +use parent 'Animal'; +sub speak { + return "Meow!"; +} + +package AnimalFactory; +sub get_animal { + my ($class, $animal_type) = @_; + if ($animal_type eq 'dog') { + return Dog->new(); + } elsif ($animal_type eq 'cat') { + return Cat->new(); + } else { + return undef; + } +} + +# Usage +my $factory = 'AnimalFactory'; +my $animal = $factory->get_animal('dog'); +print $animal->speak(), "\n"; +``` + +### Python Example + +```python +from abc import ABC, abstractmethod + +class Animal(ABC): + @abstractmethod + def speak(self): + pass + +class Dog(Animal): + def speak(self): + return "Woof!" + +class Cat(Animal): + def speak(self): + return "Meow!" + +class AnimalFactory: + @staticmethod + def get_animal(animal_type): + if animal_type == "dog": + return Dog() + elif animal_type == "cat": + return Cat() + else: + return None + +# Usage +factory = AnimalFactory() +animal = factory.get_animal("dog") +print(animal.speak()) +``` + +### Ruby Example + +```ruby +class Animal + def speak + raise NotImplementedError, 'You must implement the speak method' + end +end + +class Dog < Animal + def speak + 'Woof!' + end +end + +class Cat < Animal + def speak + 'Meow!' + end +end + +class AnimalFactory + def self.get_animal(animal_type) + case animal_type + when 'dog' + Dog.new + when 'cat' + Cat.new + else + nil + end + end +end + +# Usage +factory = AnimalFactory +animal = factory.get_animal('dog') +puts animal.speak +``` + +### Rust Example + +```rust +trait Animal { + fn speak(&self) -> String; +} + +struct Dog; +impl Animal for Dog { + fn speak(&self) -> String { + "Woof!".to_string() + } +} + +struct Cat; +impl Animal for Cat { + fn speak(&self) -> String { + "Meow!".to_string() + } +} + +enum AnimalType { + Dog, + Cat, +} + +struct AnimalFactory; + +impl AnimalFactory { + fn get_animal(animal_type: AnimalType) -> Box { + match animal_type { + AnimalType::Dog => Box::new(Dog), + AnimalType::Cat => Box::new(Cat), + } + } +} + +fn main() { + let factory = AnimalFactory; + let animal = factory.get_animal(AnimalType::Dog); + println!("{}", animal.speak()); +} +``` diff --git a/docs/Flyweight.md b/docs/Flyweight.md new file mode 100644 index 0000000..aaf5cb4 --- /dev/null +++ b/docs/Flyweight.md @@ -0,0 +1,202 @@ +## Flyweight Pattern + +The flyweight pattern is a structural design pattern that minimizes memory usage by sharing as much data as possible with similar objects. +It is particularly useful when dealing with a large number of objects that have many common attributes. By sharing common data between objects, +the flyweight pattern reduces redundancy and memory consumption, improving performance and scalability. This pattern involves creating a flyweight +object that contains shared data and a context object that contains unique data, enabling efficient management of a large number of objects. + +### Go Example + +```go +package main + +import ( + "fmt" +) + +type Flyweight struct { + sharedState string +} + +func (f *Flyweight) Operation(uniqueState string) { + fmt.Printf("Flyweight: Displaying shared (%s) and unique (%s) state.\n", f.sharedState, uniqueState) +} + +type FlyweightFactory struct { + flyweights map[string]*Flyweight +} + +func NewFlyweightFactory() *FlyweightFactory { + return &FlyweightFactory{flyweights: make(map[string]*Flyweight)} +} + +func (f *FlyweightFactory) GetFlyweight(sharedState string) *Flyweight { + if flyweight, exists := f.flyweights[sharedState]; exists { + return flyweight + } + flyweight := &Flyweight{sharedState: sharedState} + f.flyweights[sharedState] = flyweight + return flyweight +} + +// Usage +func main() { + factory := NewFlyweightFactory() + flyweight1 := factory.GetFlyweight("Shared State 1") + flyweight2 := factory.GetFlyweight("Shared State 1") + flyweight3 := factory.GetFlyweight("Shared State 2") + + flyweight1.Operation("Unique State A") + flyweight2.Operation("Unique State B") + flyweight3.Operation("Unique State C") +} +``` + +### Perl Example + +```perl +package Flyweight; +sub new { + my ($class, $shared_state) = @_; + return bless { shared_state => $shared_state }, $class; +} + +sub operation { + my ($self, $unique_state) = @_; + print "Flyweight: Displaying shared ($self->{shared_state}) and unique ($unique_state) state.\n"; +} + +package FlyweightFactory; +sub new { + my $class = shift; + return bless { flyweights => {} }, $class; +} + +sub get_flyweight { + my ($self, $shared_state) = @_; + $self->{flyweights}{$shared_state} ||= Flyweight->new($shared_state); + return $self->{flyweights}{$shared_state}; +} + +# Usage +my $factory = FlyweightFactory->new; +my $flyweight1 = $factory->get_flyweight("Shared State 1"); +my $flyweight2 = $factory->get_flyweight("Shared State 1"); +my $flyweight3 = $factory->get_flyweight("Shared State 2"); + +$flyweight1->operation("Unique State A"); +$flyweight2->operation("Unique State B"); +$flyweight3->operation("Unique State C"); +``` + +### Python Example + +```python +class Flyweight: + def __init__(self, shared_state): + self.shared_state = shared_state + + def operation(self, unique_state): + print(f"Flyweight: Displaying shared ({self.shared_state}) and unique ({unique_state}) state.") + +class FlyweightFactory: + _flyweights = {} + + def get_flyweight(self, shared_state): + if shared_state not in self._flyweights: + self._flyweights[shared_state] = Flyweight(shared_state) + return self._flyweights[shared_state] + +# Usage +factory = FlyweightFactory() +flyweight1 = factory.get_flyweight("Shared State 1") +flyweight2 = factory.get_flyweight("Shared State 1") +flyweight3 = factory.get_flyweight("Shared State 2") + +flyweight1.operation("Unique State A") +flyweight2.operation("Unique State B") +flyweight3.operation("Unique State C") +``` + +### Ruby Example + +```ruby +class Flyweight + def initialize(shared_state) + @shared_state = shared_state + end + + def operation(unique_state) + puts "Flyweight: Displaying shared (#{@shared_state}) and unique (#{unique_state}) state." + end +end + +class FlyweightFactory + def initialize + @flyweights = {} + end + + def get_flyweight(shared_state) + @flyweights[shared_state] ||= Flyweight.new(shared_state) + end +end + +# Usage +factory = FlyweightFactory.new +flyweight1 = factory.get_flyweight("Shared State 1") +flyweight2 = factory.get_flyweight("Shared State 1") +flyweight3 = factory.get_flyweight("Shared State 2") + +flyweight1.operation("Unique State A") +flyweight2.operation("Unique State B") +flyweight3.operation("Unique State C") +``` + +### Rust Example + +```rust +use std::collections::HashMap; +use std::rc::Rc; + +struct Flyweight { + shared_state: String, +} + +impl Flyweight { + fn new(shared_state: &str) -> Flyweight { + Flyweight { shared_state: shared_state.to_string() } + } + + fn operation(&self, unique_state: &str) { + println!("Flyweight: Displaying shared ({}) and unique ({}) state.", self.shared_state, unique_state); + } +} + +struct FlyweightFactory { + flyweights: HashMap>, +} + +impl FlyweightFactory { + fn new() -> FlyweightFactory { + FlyweightFactory { flyweights: HashMap::new() } + } + + fn get_flyweight(&mut self, shared_state: &str) -> Rc { + self.flyweights.entry(shared_state.to_string()) + .or_insert_with(|| Rc::new(Flyweight::new(shared_state))) + .clone() + } +} + +// Usage +fn main() { + let mut factory = FlyweightFactory::new(); + let flyweight1 = factory.get_flyweight("Shared State 1"); + let flyweight2 = factory.get_flyweight("Shared State 1"); + let flyweight3 = factory.get_flyweight("Shared State 2"); + + flyweight1.operation("Unique State A"); + flyweight2.operation("Unique State B"); + flyweight3.operation("Unique State C"); +} +``` diff --git a/docs/Iterator.md b/docs/Iterator.md new file mode 100644 index 0000000..ec95ef4 --- /dev/null +++ b/docs/Iterator.md @@ -0,0 +1,151 @@ +## Iterator Pattern + +The iterator pattern is a behavioural design pattern that provides a way to access the elements of an aggregate object sequentially without +exposing its underlying representation. This pattern encapsulates the iteration logic, allowing for flexible traversal of collections. By +providing a standard interface for traversing different types of collections, the iterator pattern promotes the single responsibility principle, +separating the traversal behaviour from the collection itself. This approach enhances the flexibility and reusability of code, making it easier +to iterate over various data structures without modifying their implementation. + + +### Go Example + +```go +package main + +import "fmt" + +type MyIterator struct { + current, end int +} + +func NewMyIterator(start, end int) *MyIterator { + return &MyIterator{current: start, end: end} +} + +func (it *MyIterator) Next() (int, bool) { + if it.current >= it.end { + return 0, false + } + val := it.current + it.current++ + return val, true +} + +func main() { + iter := NewMyIterator(0, 5) + for { + if val, ok = iter.Next(); ok { + fmt.Println(val) + } else { + break + } + } +} +``` + +### Perl Example + +```perl +package MyIterator; + +sub new { + my ($class, $start, $end) = @_; + return bless { current => $start, end => $end }, $class; +} + +sub next { + my $self = shift; + if ($self->{current} >= $self->{end}) { + return undef; + } else { + return $self->{current}++; + } +} + +# Usage +my $iter = MyIterator->new(0, 5); +while (defined(my $num = $iter->next())) { + print "$num\n"; +} +``` + +### Python Example + +```python +class MyIterator: + def __init__(self, start, end): + self.current = start + self.end = end + + def __iter__(self): + return self + + def __next__(self): + if self.current >= self.end: + raise StopIteration + else: + self.current += 1 + return self.current - 1 + +# Usage +for num in MyIterator(0, 5): + print(num) +``` + +### Ruby Example + +```ruby +class MyIterator + include Enumerable + + def initialize(start, stop) + @start = start + @stop = stop + end + + def each + (@start...@stop).each { |i| yield i } + end +end + +# Usage +MyIterator.new(0, 5).each do |num| + puts num +end +``` + +### Rust Example + +```rust +struct MyIterator { + current: usize, + end: usize, +} + +impl MyIterator { + fn new(start: usize, end: usize) -> MyIterator { + MyIterator { current: start, end } + } +} + +impl Iterator for MyIterator { + type Item = usize; + + fn next(&mut self) -> Option { + if self.current >= self.end { + None + } else { + self.current += 1; + Some(self.current - 1) + } + } +} + +// Usage +fn main() { + let iter = MyIterator::new(0, 5); + for num in iter { + println!("{}", num); + } +} +``` diff --git a/docs/Observer.md b/docs/Observer.md new file mode 100644 index 0000000..500a1b3 --- /dev/null +++ b/docs/Observer.md @@ -0,0 +1,224 @@ +## Observer Pattern + +The observer pattern is a behavioural design pattern that defines a one-to-many dependency between objects, ensuring that when one object +changes state, all its dependents are notified and updated automatically. This pattern is commonly used in event-driven programming to +implement distributed event-handling systems. By promoting loose coupling between the subject (the object being observed) and the observers +(the objects watching for changes), the observer pattern enhances flexibility and reusability. It allows for dynamic subscription and notification, +making it easier to manage and coordinate the behaviour of multiple objects in response to state changes. + +### Go Example + +```go +package main + +import "fmt" + +type Observer interface { + Update(message string) +} + +type Subject struct { + observers []Observer +} + +func (s *Subject) Attach(observer Observer) { + s.observers = append(s.observers, observer) +} + +func (s *Subject) Detach(observer Observer) { + for i, o := range s.observers { + if o == observer { + s.observers = append(s.observers[:i], s.observers[i+1:]...) + break + } + } +} + +func (s *Subject) Notify(message string) { + for _, observer := range s.observers { + observer.Update(message) + } +} + +type ConcreteObserver struct{} + +func (co *ConcreteObserver) Update(message string) { + fmt.Println("Received message:", message) +} + +func main() { + subject := &Subject{} + observer := &ConcreteObserver{} + + subject.Attach(observer) + subject.Notify("Hello, World!") +} +``` + +### Perl Example + +```perl +package Subject; +sub new { + my $class = shift; + my $self = { observers => [] }; + bless $self, $class; + return $self; +} + +sub attach { + my ($self, $observer) = @_; + push @{$self->{observers}}, $observer; +} + +sub detach { + my ($self, $observer) = @_; + @{$self->{observers}} = grep { $_ != $observer } @{$self->{observers}}; +} + +sub notify { + my ($self, $message) = @_; + $_->update($message) for @{$self->{observers}}; +} + +package Observer; +sub update { + my ($self, $message) = @_; + # To be implemented by concrete observers +} + +package ConcreteObserver; +use base 'Observer'; + +sub update { + my ($self, $message) = @_; + print "Received message: $message\n"; +} + +# Usage +my $subject = Subject->new; +my $observer = ConcreteObserver->new; + +$subject->attach($observer); +$subject->notify("Hello, World!"); +``` + +### Python Example + +```python +class Subject: + def __init__(self): + self._observers = [] + + def attach(self, observer): + self._observers.append(observer) + + def detach(self, observer): + self._observers.remove(observer) + + def notify(self, message): + for observer in self._observers: + observer.update(message) + +class Observer: + def update(self, message): + pass + +class ConcreteObserver(Observer): + def update(self, message): + print(f"Received message: {message}") + +# Usage +subject = Subject() +observer = ConcreteObserver() + +subject.attach(observer) +subject.notify("Hello, World!") +``` + +### Ruby Example + +```ruby +class Subject + def initialize + @observers = [] + end + + def attach(observer) + @observers << observer + end + + def detach(observer) + @observers.delete(observer) + end + + def notify(message) + @observers.each { |observer| observer.update(message) } + end +end + +class Observer + def update(message) + # To be implemented by concrete observers + end +end + +class ConcreteObserver < Observer + def update(message) + puts "Received message: #{message}" + end +end + +# Usage +subject = Subject.new +observer = ConcreteObserver.new + +subject.attach(observer) +subject.notify("Hello, World!") +``` + +### Rust Example + +```rust +trait Observer { + fn update(&self, message: &str); +} + +struct Subject { + observers: Vec>, +} + +impl Subject { + fn new() -> Self { + Subject { observers: Vec::new() } + } + + fn attach(&mut self, observer: Box) { + self.observers.push(observer); + } + + fn detach(&mut self, index: usize) { + self.observers.remove(index); + } + + fn notify(&self, message: &str) { + for observer in &self.observers { + observer.update(message); + } + } +} + +struct ConcreteObserver; + +impl Observer for ConcreteObserver { + fn update(&self, message: &str) { + println!("Received message: {}", message); + } +} + +fn main() { + let mut subject = Subject::new(); + let observer = Box::new(ConcreteObserver); + + subject.attach(observer); + subject.notify("Hello, World!");``` diff --git a/docs/Proxy.md b/docs/Proxy.md new file mode 100644 index 0000000..3f3895b --- /dev/null +++ b/docs/Proxy.md @@ -0,0 +1,215 @@ +## Proxy Pattern + +The proxy pattern is a structural design pattern that provides a surrogate or placeholder for another object to control access to it. +This pattern is used to add an extra level of indirection to support controlled access, lazy initialization, logging, and other similar +tasks. By implementing the same interface as the underlying object, the proxy can act as a stand-in, forwarding requests to the real object +while adding its behaviour. This approach enhances control over the underlying object, promoting flexibility and maintainability in software design. + +### Go Example + +```go +package main + +import "fmt" + +type Subject interface { + Request() string +} + +type RealSubject struct{} + +func (r *RealSubject) Request() string { + return "RealSubject: Handling request." +} + +type Proxy struct { + realSubject *RealSubject +} + +func (p *Proxy) Request() string { + if p.checkAccess() { + result := p.realSubject.Request() + p.logAccess() + return result + } + return "Access denied." +} + +func (p *Proxy) checkAccess() bool { + fmt.Println("Proxy: Checking access prior to firing a real request.") + return true +} + +func (p *Proxy) logAccess() { + fmt.Println("Proxy: Logging the time of request.") +} + +func main() { + realSubject := &RealSubject{} + proxy := &Proxy{realSubject} + fmt.Println(proxy.Request()) +} +``` + +### Perl Example + +```perl +package RealSubject; +sub new { + my $class = shift; + bless {}, $class; +} + +sub request { + return "RealSubject: Handling request."; +} + +package Proxy; +sub new { + my ($class, $real_subject) = @_; + bless { real_subject => $real_subject }, $class; +} + +sub request { + my $self = shift; + if ($self->check_access()) { + my $result = $self->{real_subject}->request(); + $self->log_access(); + return $result; + } +} + +sub check_access { + print "Proxy: Checking access prior to firing a real request.\n"; + return 1; +} + +sub log_access { + print "Proxy: Logging the time of request.\n"; +} + +package main; +my $real_subject = RealSubject->new(); +my $proxy = Proxy->new($real_subject); +print $proxy->request() . "\n"; +``` + +### Python Example + +```python +class RealSubject: + def request(self): + return "RealSubject: Handling request." + +class Proxy: + def __init__(self, real_subject): + self._real_subject = real_subject + + def request(self): + if self.check_access(): + result = self._real_subject.request() + self.log_access() + return result + + def check_access(self): + print("Proxy: Checking access prior to firing a real request.") + return True + + def log_access(self): + print("Proxy: Logging the time of request.") + +if __name__ == "__main__": + real_subject = RealSubject() + proxy = Proxy(real_subject) + print(proxy.request()) +``` + +### Ruby Example + +```ruby +class RealSubject + def request + "RealSubject: Handling request." + end +end + +class Proxy + def initialize(real_subject) + @real_subject = real_subject + end + + def request + if check_access + result = @real_subject.request + log_access + result + end + end + + def check_access + puts "Proxy: Checking access prior to firing a real request." + true + end + + def log_access + puts "Proxy: Logging the time of request." + end +end + +real_subject = RealSubject.new +proxy = Proxy.new(real_subject) +puts proxy.request +``` + +### Rust Example + +```rust +trait Subject { + fn request(&self) -> String; +} + +struct RealSubject; + +impl Subject for RealSubject { + fn request(&self) -> String { + "RealSubject: Handling request.".to_string() + } +} + +struct Proxy { + real_subject: RealSubject, +} + +impl Proxy { + fn new(real_subject: RealSubject) -> Self { + Proxy { real_subject } + } + + fn check_access(&self) -> bool { + println!("Proxy: Checking access prior to firing a real request."); + true + } + + fn log_access(&self) { + println!("Proxy: Logging the time of request."); + } +} + +impl Subject for Proxy { + fn request(&self) -> String { + if self.check_access() { + let result = self.real_subject.request(); + self.log_access(); + result + } else { + "Access denied.".to_string() + } + } +} + +fn main() { + let real_subject = RealSubject; + let proxy = Proxy::new(real_subject); + println!("{}", proxy.request()); +} +``` diff --git a/docs/Singleton.md b/docs/Singleton.md new file mode 100644 index 0000000..5c44168 --- /dev/null +++ b/docs/Singleton.md @@ -0,0 +1,135 @@ +## Singleton Pattern + +The singleton pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to it. +This pattern is useful for managing shared resources or coordinating actions across an application. By controlling the instantiation process, +the singleton pattern prevents the creation of multiple instances, ensuring that all requests for the instance are directed to the same object. +This approach promotes resource management and consistency, making it easier to manage and coordinate shared resources effectively. + +### Go Example + +```go +package main + +import ( + "fmt" + "sync" +) + +type Singleton struct{} + +var instance *Singleton +var once sync.Once + +func GetInstance() *Singleton { + once.Do(func() { + instance = &Singleton{} + }) + return instance +} + +func main() { + singleton1 := GetInstance() + singleton2 := GetInstance() + + fmt.Println(singleton1 == singleton2) // Output: true +} +``` + +### Perl Example + +```perl +package Singleton; + +my $instance; + +sub new { + my $class = shift; + unless (defined $instance) { + $instance = bless {}, $class; + } + return $instance; +} + +# Usage +my $singleton1 = Singleton->new(); +my $singleton2 = Singleton->new(); + +print $singleton1 == $singleton2 ? "true\n" : "false\n"; # Output: true +``` + +### Python Example + +```python +class Singleton: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) + return cls._instance + +# Usage +singleton1 = Singleton() +singleton2 = Singleton() + +print(singleton1 is singleton2) # Output: True +``` + +### Ruby Example + +```ruby +class Singleton + @instance = nil + + private_class_method :new + + def self.instance + @instance ||= new + end +end + +# Usage +singleton1 = Singleton.instance +singleton2 = Singleton.instance + +puts singleton1.equal?(singleton2) # Output: true +``` + +### Rust Example + +```rust +use std::sync::{Arc, Mutex}; +use std::sync::Once; + +struct Singleton { + // Add fields here +} + +impl Singleton { + fn new() -> Self { + Singleton { + // Initialize fields here + } + } +} + +static mut SINGLETON: Option>> = None; +static ONCE: Once = Once::new(); + +fn singleton_instance() -> Arc> { + unsafe { + ONCE.call_once(|| { + let singleton = Singleton::new(); + SINGLETON = Some(Arc::new(Mutex::new(singleton))); + }); + SINGLETON.clone().unwrap() + } +} + +fn main() { + let singleton1 = singleton_instance(); + let singleton2 = singleton_instance(); + + println!("{}", Arc::ptr_eq(&singleton1, &singleton2)); // Output: true +} +``` diff --git a/docs/Strategy.md b/docs/Strategy.md new file mode 100644 index 0000000..a3746be --- /dev/null +++ b/docs/Strategy.md @@ -0,0 +1,230 @@ +## Strategy Pattern + +The strategy pattern is a behavioural design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. +This pattern allows the algorithm to vary independently from the clients that use it, promoting flexibility and reusability. By defining a common +interface for all supported algorithms, the strategy pattern enables the client to choose the appropriate algorithm at runtime. This approach +enhances the extensibility of the system, allowing new algorithms to be added without modifying the existing code. The strategy pattern is +particularly useful for scenarios where multiple algorithms are applicable and the best one to use can vary based on context or user choice. + +### Go Example + +```go +package main + +import "fmt" + +// Strategy is the strategy interface +type Strategy interface { + Execute(a, b int) int +} + +// AddStrategy is a concrete strategy that adds two numbers +type AddStrategy struct{} + +func (s AddStrategy) Execute(a, b int) int { + return a + b +} + +// SubtractStrategy is a concrete strategy that subtracts two numbers +type SubtractStrategy struct{} + +func (s SubtractStrategy) Execute(a, b int) int { + return a - b +} + +// Context is the context that uses a strategy +type Context struct { + strategy Strategy +} + +func (c *Context) SetStrategy(strategy Strategy) { + c.strategy = strategy +} + +func (c *Context) ExecuteStrategy(a, b int) int { + return c.strategy.Execute(a, b) +} + +func main() { + context := Context{strategy: AddStrategy{}} + fmt.Println(context.ExecuteStrategy(5, 3)) // Output: 8 + + context.SetStrategy(SubtractStrategy{}) + fmt.Println(context.ExecuteStrategy(5, 3)) // Output: 2 +} +``` + +### Perl Example + +```perl +package Strategy; +sub new { + my ($class) = @_; + return bless {}, $class; +} +sub execute { + die "This method should be overridden"; +} + +package AddStrategy; +our @ISA = qw(Strategy); +sub execute { + my ($self, $a, $b) = @_; + return $a + $b; +} + +package SubtractStrategy; +our @ISA = qw(Strategy); +sub execute { + my ($self, $a, $b) = @_; + return $a - $b; +} + +package Context; +sub new { + my ($class, $strategy) = @_; + return bless { strategy => $strategy }, $class; +} +sub set_strategy { + my ($self, $strategy) = @_; + $self->{strategy} = $strategy; +} +sub execute_strategy { + my ($self, $a, $b) = @_; + return $self->{strategy}->execute($a, $b); +} + +# Usage +my $context = Context->new(AddStrategy->new); +print $context->execute_strategy(5, 3), "\n"; # Output: 8 + +$context->set_strategy(SubtractStrategy->new); +print $context->execute_strategy(5, 3), "\n"; # Output: 2 +``` + +### Python Example + +```python +from abc import ABC, abstractmethod + +class Strategy(ABC): + @abstractmethod + def execute(self, a, b): + pass + +class AddStrategy(Strategy): + def execute(self, a, b): + return a + b + +class SubtractStrategy(Strategy): + def execute(self, a, b): + return a - b + +class Context: + def __init__(self, strategy: Strategy): + self._strategy = strategy + + def set_strategy(self, strategy: Strategy): + self._strategy = strategy + + def execute_strategy(self, a, b): + return self._strategy.execute(a, b) + +# Usage +context = Context(AddStrategy()) +print(context.execute_strategy(5, 3)) # Output: 8 + +context.set_strategy(SubtractStrategy()) +print(context.execute_strategy(5, 3)) # Output: 2 +``` + +### Ruby Example + +```ruby +class Strategy + def execute(a, b) + raise NotImplementedError, 'This method should be overridden' + end +end + +class AddStrategy < Strategy + def execute(a, b) + a + b + end +end + +class SubtractStrategy < Strategy + def execute(a, b) + a - b + end +end + +class Context + def initialize(strategy) + @strategy = strategy + end + + def set_strategy(strategy) + @strategy = strategy + end + + def execute_strategy(a, b) + @strategy.execute(a, b) + end +end + +# Usage +context = Context.new(AddStrategy.new) +puts context.execute_strategy(5, 3) # Output: 8 + +context.set_strategy(SubtractStrategy.new) +puts context.execute_strategy(5, 3) # Output: 2 +``` + +### Rust Example + +```rust +trait Strategy { + fn execute(&self, a: i32, b: i32) -> i32; +} + +struct AddStrategy; +impl Strategy for AddStrategy { + fn execute(&self, a: i32, b: i32) -> i32 { + a + b + } +} + +struct SubtractStrategy; +impl Strategy for SubtractStrategy { + fn execute(&self, a: i32, b: i32) -> i32 { + a - b + } +} + +struct Context { + strategy: Box, +} + +impl Context { + fn new(strategy: Box) -> Self { + Context { strategy } + } + + fn set_strategy(&mut self, strategy: Box) { + self.strategy = strategy; + } + + fn execute_strategy(&self, a: i32, b: i32) -> i32 { + self.strategy.execute(a, b) + } +} + +fn main() { + let mut context = Context::new(Box::new(AddStrategy)); + println!("{}", context.execute_strategy(5, 3)); // Output: 8 + + context.set_strategy(Box::new(SubtractStrategy)); + println!("{}", context.execute_strategy(5, 3)); // Output: 2 +} +```