Joel Cuevas Estrada - 22210298
#include < iostream>
#include < string>
#include < vector>
#include < iomanip>
using namespace std ;
struct Smartphone {
string name;
string brand;
double price;
int stock;
};
class InventoryAndBilling {
public:
vector<Smartphone*> phones;
double totalSales = 0 ;
int invoiceCount = 0 ;
void addPhone (string n, string b, double p, int s) {
Smartphone* ph = new Smartphone ();
ph->name = n;
ph->brand = b;
ph->price = p;
ph->stock = s;
phones.push_back (ph);
cout << " Added phone: " << n << " - Stock: " << s << endl;
}
void showPhones () {
cout << " \n === INVENTORY ===\n " ;
for (int i = 0 ; i < phones.size (); i++) {
cout << i << " ) " << phones[i]->brand << " " << phones[i]->name
<< " | $" << phones[i]->price
<< " | Stock: " << phones[i]->stock << endl;
}
}
void sellPhone (int index, int quantity) {
if (index < 0 || index >= phones.size ()) {
cout << " Invalid index" << endl;
return ;
}
if (phones[index]->stock < quantity) {
cout << " Not enough stock!" << endl;
return ;
}
phones[index]->stock -= quantity;
double total = phones[index]->price * quantity;
totalSales += total;
invoiceCount++;
cout << " \n === INVOICE #" << invoiceCount << " ===" << endl;
cout << phones[index]->brand << " " << phones[index]->name << endl;
cout << " Qty: " << quantity << endl;
cout << " Total: $" << fixed << setprecision (2 ) << total << endl;
cout << " ======================" << endl;
}
void removePhone (int index) {
if (index < 0 || index >= phones.size ()) {
cout << " Invalid index" << endl;
return ;
}
cout << " Removing: " << phones[index]->name << endl;
delete phones[index];
phones.erase (phones.begin () + index);
}
void showSummary () {
cout << " \n === SUMMARY ===\n " ;
cout << " Total invoices: " << invoiceCount << endl;
cout << " Total sales: $" << totalSales << endl;
cout << " Remaining phones: " << phones.size () << endl;
}
// Duplicated logic: manually adjusts stock (used nowhere properly)
void adjustStock (string phoneName, int newStock) {
for (int i = 0 ; i < phones.size (); i++) {
if (phones[i]->name == phoneName) {
phones[i]->stock = newStock;
cout << " Stock updated for " << phoneName << " : " << newStock << endl;
break ;
}
}
}
// Bad smell: mixed logic for billing and inventory reports
void exportReport () {
cout << " \n === FULL REPORT ===\n " ;
for (int i = 0 ; i < phones.size (); i++) {
cout << phones[i]->brand << " " << phones[i]->name
<< " | Stock: " << phones[i]->stock
<< " | Price: " << phones[i]->price << endl;
}
cout << " Total sales: $" << totalSales << endl;
cout << " Invoices made: " << invoiceCount << endl;
cout << " ======================\n " ;
}
// Memory leak risk if not called
~InventoryAndBilling () {
for (auto p : phones) {
delete p;
}
}
};
int main () {
InventoryAndBilling store;
store.addPhone (" iPhone 15" , " Apple" , 1099.99 , 10 );
store.addPhone (" Galaxy S24" , " Samsung" , 899.49 , 7 );
store.addPhone (" Pixel 9" , " Google" , 799.99 , 5 );
store.showPhones ();
store.sellPhone (0 , 2 );
store.sellPhone (2 , 1 );
store.removePhone (1 );
store.adjustStock (" iPhone 15" , 5 );
store.exportReport ();
store.showSummary ();
}
God Class = InventoryAndBilling mezcla responsabilidades de inventario y facturación.
Memory leaks = Usa punteros crudos en lugar de unique_ptr o valores.
Duplicated code = Lógica repetida en adjustStock, removePhone, showPhones.
No encapsulación = Acceso público a casi todo.
Magic numbers = Precios y cantidades “hardcodeados” en main().
Mixed responsibilities = Facturación y gestión del inventario en la misma clase.
Tight coupling = Todo depende directamente del vector global phones.
Inconsistent naming = exportReport, showSummary, showPhones — sin convención uniforme.
Refactorizado con SRP (Single Responsability Principle)
#include < iostream>
#include < string>
#include < vector>
#include < iomanip>
using namespace std ;
struct Smartphone {
string name;
string brand;
double price;
int stock;
};
// ======================= INVENTARIO =======================
class Inventory {
private:
vector<Smartphone> phones;
public:
void addPhone (const string& name, const string& brand, double price, int stock) {
phones.push_back ({name, brand, price, stock});
cout << " ✅ Agregado: " << brand << " " << name << " (Stock: " << stock << " )\n " ;
}
void removePhone (int index) {
if (index < 0 || index >= phones.size ()) {
cout << " ❌ Índice inválido.\n " ;
return ;
}
cout << " 🗑️ Eliminado: " << phones[index].brand << " " << phones[index].name << " \n " ;
phones.erase (phones.begin () + index);
}
void updateStock (const string& name, int newStock) {
for (auto & phone : phones) {
if (phone.name == name) {
phone.stock = newStock;
cout << " 🔄 Stock actualizado para " << name << " : " << newStock << " \n " ;
return ;
}
}
cout << " ⚠️ No se encontró el teléfono: " << name << " \n " ;
}
vector<Smartphone>& getPhones () { return phones; }
const vector<Smartphone>& getPhones () const { return phones; }
void showPhones () const {
cout << " \n 📦 INVENTARIO:\n " ;
for (size_t i = 0 ; i < phones.size (); ++i) {
const auto & p = phones[i];
cout << i << " ) " << p.brand << " " << p.name
<< " | $" << p.price << " | Stock: " << p.stock << " \n " ;
}
}
};
// ======================= FACTURACIÓN =======================
class Billing {
private:
double totalSales = 0 ;
int invoiceCount = 0 ;
public:
void sellPhone (Inventory& inventory, int index, int quantity) {
auto & phones = inventory.getPhones ();
if (index < 0 || index >= phones.size ()) {
cout << " ❌ Índice inválido.\n " ;
return ;
}
Smartphone& phone = phones[index];
if (phone.stock < quantity) {
cout << " ⚠️ Stock insuficiente.\n " ;
return ;
}
phone.stock -= quantity;
double total = phone.price * quantity;
totalSales += total;
invoiceCount++;
printInvoice (phone, quantity, total);
}
void printInvoice (const Smartphone& phone, int quantity, double total) const {
cout << " \n 🧾 FACTURA #" << invoiceCount << " \n " ;
cout << " Producto: " << phone.brand << " " << phone.name << " \n " ;
cout << " Cantidad: " << quantity << " \n " ;
cout << " Total: $" << fixed << setprecision (2 ) << total << " \n " ;
cout << " ============================\n " ;
}
double getTotalSales () const { return totalSales; }
int getInvoiceCount () const { return invoiceCount; }
};
// ======================= REPORTES =======================
class Report {
public:
static void showSummary (const Billing& billing, const Inventory& inventory) {
cout << " \n 📊 RESUMEN:\n " ;
cout << " Facturas generadas: " << billing.getInvoiceCount () << " \n " ;
cout << " Ventas totales: $" << billing.getTotalSales () << " \n " ;
cout << " Productos restantes: " << inventory.getPhones ().size () << " \n " ;
}
static void exportReport (const Billing& billing, const Inventory& inventory) {
cout << " \n 🧾 REPORTE COMPLETO\n " ;
for (const auto & p : inventory.getPhones ()) {
cout << p.brand << " " << p.name
<< " | Stock: " << p.stock
<< " | Precio: $" << p.price << " \n " ;
}
cout << " Total ventas: $" << billing.getTotalSales () << " \n " ;
cout << " Facturas: " << billing.getInvoiceCount () << " \n " ;
cout << " ============================\n " ;
}
};
// ======================= PROGRAMA PRINCIPAL =======================
int main () {
Inventory inventory;
Billing billing;
inventory.addPhone (" iPhone 15" , " Apple" , 1099.99 , 10 );
inventory.addPhone (" Galaxy S24" , " Samsung" , 899.49 , 7 );
inventory.addPhone (" Pixel 9" , " Google" , 799.99 , 5 );
inventory.showPhones ();
billing.sellPhone (inventory, 0 , 2 );
billing.sellPhone (inventory, 2 , 1 );
inventory.removePhone (1 );
inventory.updateStock (" iPhone 15" , 5 );
Report::exportReport (billing, inventory);
Report::showSummary (billing, inventory);
}
Direfencias antes y despues de factorización
Antes (Espagueti)
Después (SRP aplicado)
Una sola clase hacía todo (God Class)
Se separan 3 clases con responsabilidades claras.
Mezcla de inventario y facturación
Cada clase tiene un único motivo de cambio.
Dificultad para mantener y probar
Código modular, fácil de extender o testear.
Duplicación de lógica y variables globales
Encapsulación y métodos bien definidos.
Compilacion y ejecucion del codigo