spec

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 25, 2025 License: MIT Imports: 1 Imported by: 0

README

spec

Simple implementation of the Composite Specification pattern (archive). Only conjunction operation (and) is implemented for simplicity and better performance.

Also it can provide textual description what specification rule is not satisfied by using an error interface instead of a bool predicate.

Example

Define class:

type Man struct {
    Age    int
    Mortal bool
    Diet   []string
}

Define specifications:

type ImmortalSpec struct{}

func (ImmortalSpec) SatisfiedBy(man Man) error {
    if !man.Mortal {
        return nil
    }

    return fmt.Errorf("expected to be immortal")
}

type AncientSpec struct{}

func (AncientSpec) SatisfiedBy(man Man) error {
    const n = 500

    if man.Age >= n {
        return nil
    }

    return fmt.Errorf("expected to be older than %d years", n)
}

type DietSpec struct {
    Meals map[string]struct{}
}

func (diet DietSpec) SatisfiedBy(man Man) error {
    for _, meal := range man.Diet {
        if _, ok := diet.Meals[meal]; !ok {
            return fmt.Errorf("expected diet: %v", diet.Meals)
        }
    }

    return nil
}

Test specification is satisfied:

// Define composite specification.
vampireSpec := spec.And(
    ImmortalSpec{},
    AncientSpec{},
    DietSpec{map[string]struct{}{"blood": {}}},
)

// Define man instance.
man := Man{
    Age:    200,
    Mortal: true,
    Diet:   []string{"meat", "apple"},
}

// Test the man is a vampire.
fmt.Println(vampireSpec.SatisfiedBy(man))

// Output:
// expected to be immortal
// expected to be older than 500 years
// expected diet: map[blood:{}]

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Conjunction

type Conjunction[T any] struct {
	// contains filtered or unexported fields
}

func And

func And[T any](x, y Specification[T], z ...Specification[T]) Conjunction[T]

func (Conjunction[T]) SatisfiedBy

func (conj Conjunction[T]) SatisfiedBy(value T) error

type Specification

type Specification[T any] interface {
	SatisfiedBy(value T) error
}
Example
package main

import (
	"fmt"

	"github.com/WinPooh32/spec"
)

type Man struct {
	Age    int
	Mortal bool
	Diet   []string
}

type ImmortalSpec struct{}

func (ImmortalSpec) SatisfiedBy(man Man) error {
	if !man.Mortal {
		return nil
	}

	return fmt.Errorf("expected to be immortal")
}

type AncientSpec struct{}

func (AncientSpec) SatisfiedBy(man Man) error {
	const n = 500

	if man.Age >= n {
		return nil
	}

	return fmt.Errorf("expected to be older than %d years", n)
}

type DietSpec struct {
	Meals map[string]struct{}
}

func (diet DietSpec) SatisfiedBy(man Man) error {
	for _, meal := range man.Diet {
		if _, ok := diet.Meals[meal]; !ok {
			return fmt.Errorf("expected diet: %v", diet.Meals)
		}
	}

	return nil
}

func main() {
	// Define composite specification.
	vampireSpec := spec.And(
		ImmortalSpec{},
		AncientSpec{},
		DietSpec{map[string]struct{}{"blood": {}}},
	)

	// Define man instance.
	man := Man{
		Age:    200,
		Mortal: true,
		Diet:   []string{"meat", "apple"},
	}

	// Test the man is a vampire.
	fmt.Println(vampireSpec.SatisfiedBy(man))

}
Output:

expected to be immortal
expected to be older than 500 years
expected diet: map[blood:{}]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL