Skip to content

Instantly share code, notes, and snippets.

@sarath-soman
Created October 3, 2024 22:36
Show Gist options
  • Save sarath-soman/54d5929f3ce40e1c1c48d94da77a5e6a to your computer and use it in GitHub Desktop.
Save sarath-soman/54d5929f3ce40e1c1c48d94da77a5e6a to your computer and use it in GitHub Desktop.
Data oriented programming - Examples | TS | C++ | Java
----------------------------------------TS----------------------------------------
import { z } from "zod";
// User Schema using Zod
// Principle 4: Separating data schema from data representation
// The schema is defined using Zod, allowing us to validate the structure of User data independently of its representation in the program.
const UserSchema = z.object({
id: z.number(),
name: z.string(),
age: z.number(),
}).readonly();
type User = z.infer<typeof UserSchema>;
// UserCollection Schema
// Principle 2: Representing data with generic data structures
// The collection of users is represented using a generic array structure, keeping the data representation simple and flexible.
type UserCollection = User[];
// Initial data
const users: UserCollection = [
{ id: 1, name: "Alice", age: 25 },
{ id: 2, name: "Bob", age: 30 },
];
// Validate initial data
// Principle 1: Separating code (behavior) from data
// The validation of data is separated from the data itself, ensuring data integrity without coupling validation logic directly to the data representation.
users.forEach(user => { ...user });
// Behavior: Function to add a user to the collection
// Principle 3: Treating data as immutable
// This function returns a new collection with the added user, rather than modifying the existing collection, ensuring immutability.
function addUser(users: UserCollection, newUser: User): UserCollection {
UserSchema.parse(newUser);
return [...users, newUser];
}
// Behavior: Function to update a user's age
// Principle 3: Treating data as immutable
// This function returns a new collection with the updated user, ensuring the original data remains unchanged.
function updateUserAge(users: UserCollection, userId: number, newAge: number): UserCollection {
return users.map(user =>
user.id === userId ? { ...user, age: newAge } : user
);
}
// Add a new user (after schema validation)
const updatedUsers = addUser(users, { id: 3, name: "Charlie", age: 22 });
// Update age of an existing user
const usersAfterUpdate = updateUserAge(updatedUsers, 1, 26);
console.log(usersAfterUpdate);
----------------------------------------TS----------------------------------------
----------------------------------------CPP---------------------------------------
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
// Principle 4: Separating data schema from data representation
// User structure definition
struct User {
const int id;
const std::string name;
const int age;
};
// UserCollection Schema
// Principle 2: Representing data with generic data structures
// The collection of users is represented using a generic vector structure, keeping the data representation simple and flexible.
typedef std::vector<User> UserCollection;
// Behavior: Function to add a user to the collection
// Principle 3: Treating data as immutable
// This function returns a new collection with the added user, rather than modifying the existing collection, ensuring immutability.
UserCollection addUser(const UserCollection& users, const User& newUser) {
UserCollection updatedUsers = users;
updatedUsers.push_back(newUser);
return updatedUsers;
}
// Behavior: Function to update a user's age
// Principle 3: Treating data as immutable
// This function returns a new collection with the updated user, ensuring the original data remains unchanged.
UserCollection updateUserAge(const UserCollection& users, int userId, int newAge) {
UserCollection updatedUsers;
for (const auto& user : users) {
if (user.id == userId) {
updatedUsers.push_back({user.id, user.name, newAge});
} else {
updatedUsers.push_back(user);
}
}
return updatedUsers;
}
int main() {
// Initial data
UserCollection users = {
{1, "Alice", 25},
{2, "Bob", 30}
};
// Add a new user
User newUser = {3, "Charlie", 22};
UserCollection updatedUsers = addUser(users, newUser);
// Update age of an existing user
UserCollection usersAfterUpdate = updateUserAge(updatedUsers, 1, 26);
// Print updated users
for (const auto& user : usersAfterUpdate) {
std::cout << "ID: " << user.id << ", Name: " << user.name << ", Age: " << user.age << std::endl;
}
return 0;
}
----------------------------------------CPP---------------------------------------
----------------------------------------Java--------------------------------------
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.ConstraintViolation;
import javax.validation.constraints.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
// User class definition
// Principle 4: Separating data schema from data representation
// The User class represents the data, while the validation logic is handled separately through the Builder class.
final class User {
private final int id;
private final String name;
private final int age;
// Private constructor, to ensure User instances can only be created via the Builder
private User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "ID: " + id + ", Name: " + name + ", Age: " + age;
}
// Builder Class for User
// Principle 4: Separating schema validation from data representation
// The Builder class handles validation, ensuring that User instances are always valid upon creation.
public static class Builder {
@Positive(message = "ID must be positive.")
private int id;
@NotBlank(message = "Name cannot be blank.")
private String name;
@Min(value = 0, message = "Age must be at least 0.")
@Max(value = 120, message = "Age must be less than or equal to 120.")
private int age;
public Builder setId(int id) {
this.id = id;
return this;
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public User build() {
// Perform validation before creating User
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Builder>> violations = validator.validate(this);
if (!violations.isEmpty()) {
StringBuilder errorMessage = new StringBuilder();
for (ConstraintViolation<Builder> violation : violations) {
errorMessage.append(violation.getMessage()).append("\n");
}
throw new IllegalArgumentException("User validation failed: \n" + errorMessage);
}
return new User(id, name, age);
}
}
}
// UserCollection Schema
// Principle 2: Representing data with generic data structures
// The collection of users is represented using a generic list structure, keeping the data representation simple and flexible.
class UserCollection {
private final List<User> users;
public UserCollection(List<User> users) {
this.users = Collections.unmodifiableList(new ArrayList<>(users));
}
public List<User> getUsers() {
return users;
}
}
public class Main {
// Behavior: Function to add a user to the collection
// Principle 3: Treating data as immutable
// This function returns a new collection with the added user, rather than modifying the existing collection, ensuring immutability.
public static UserCollection addUser(UserCollection userCollection, User newUser) {
List<User> updatedUsers = new ArrayList<>(userCollection.getUsers());
updatedUsers.add(newUser);
return new UserCollection(updatedUsers);
}
// Behavior: Function to update a user's age
// Principle 3: Treating data as immutable
// This function returns a new collection with the updated user, ensuring the original data remains unchanged.
public static UserCollection updateUserAge(UserCollection userCollection, int userId, int newAge) {
List<User> updatedUsers = new ArrayList<>();
for (User user : userCollection.getUsers()) {
if (user.getId() == userId) {
updatedUsers.add(new User.Builder()
.setId(user.getId())
.setName(user.getName())
.setAge(newAge)
.build());
} else {
updatedUsers.add(user);
}
}
return new UserCollection(updatedUsers);
}
public static void main(String[] args) {
// Initial data
List<User> initialUsers = List.of(
new User.Builder().setId(1).setName("Alice").setAge(25).build(),
new User.Builder().setId(2).setName("Bob").setAge(30).build()
);
UserCollection users = new UserCollection(initialUsers);
// Add a new user
User newUser = new User.Builder().setId(3).setName("Charlie").setAge(22).build();
UserCollection updatedUsers = addUser(users, newUser);
// Update age of an existing user
UserCollection usersAfterUpdate = updateUserAge(updatedUsers, 1, 26);
// Print updated users
for (User user : usersAfterUpdate.getUsers()) {
System.out.println(user);
}
}
}
----------------------------------------Java--------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment