-
-
Save sarath-soman/54d5929f3ce40e1c1c48d94da77a5e6a to your computer and use it in GitHub Desktop.
Data oriented programming - Examples | TS | C++ | Java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
----------------------------------------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